LOJ3049 「十二省联考 2019」字符串问题

题目传送门

分析:
最暴力的做法,A向可以控制的B连边,B向以它为前缀的A连边
然后拓扑排序求一条路径的最大值
复杂度\(O(n^2)\)
明显要优化连边,这里给出两种解法:

SAM优化建边:
这里要前缀关系,我们反向建SAM将其变成后缀关系
对于每个串\([l,r]\),我们先找到\(r\)结尾的前缀在SAM上的位置,通过Parent树上倍增找到\([l,r]\)所在的endpos集合的点
对于每个Parent树上的点,维护一个vector,保存在这个点上的A和B
同一个点上的A和B,先按len为关键字从小到大排序,再将B优先放前面,A优先放后面
每个点向它子串集合中最短的连边,B向比他长的并且比下一个B短的A连边,Parent树父亲集合中最长的串向儿子连边
再把控制关系连上去
然后直接跑拓扑排序,时空复杂度都会从\(O(n^2)\)变成\(O(n)\)
写起来细节挺多的有点恶心

后缀数组+主席树优化建边:
我们先用原串跑一次后缀数组
B是A的前缀只需要\(min_{i=rk[lB]+1}^{rk[lA]}height[i]\geq len[B]\)
我们对于每个B,满足上述关系的一定是一个包含\(lB\)的区间
这个可以二分+\(O(1)\)RMQ解决
然后线段树优化建边?不行
发现A的长度如果小于B就会出现问题
那就用主席树
把子串长度从大到小排序,一个一个加入再优化建边就可以了
然后把控制关系也加进去
跑拓扑排序,但是时空复杂度都是\(O(nlogn)\),很卡常

代码是第一种做法的:
(后缀数组是个好东西,我有头发的时候天天写)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<map>

#define maxn 400005
#define INF 0x3f3f3f3f

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,m,na,nb;
int fir[maxn<<2],nxt[maxn<<3],to[maxn<<3],cnt;
struct node{
	int fa,nxt[26],len;
}t[maxn];
int lst,tot,cur;
char s[maxn];
int len[maxn<<2],Ed[maxn<<2],pos[maxn<<2];
int Id[maxn<<2],f[maxn][20],In[maxn<<2];
int A[maxn<<2],B[maxn<<2];
long long F[maxn<<2];
vector<int>g[maxn<<2];
inline bool cmp(int x,int y){return len[x]==len[y]?pos[x]<pos[y]:len[x]<len[y];}

inline void insert(int c)
{
	int p=lst,np=lst=++tot;
	t[np].len=t[p].len+1;
	while(p&&!t[p].nxt[c])t[p].nxt[c]=np,p=t[p].fa;
	if(!p)t[np].fa=1;
	else
	{
		int q=t[p].nxt[c];
		if(t[q].len==t[p].len+1)t[np].fa=q;
		else
		{
			int nq=++tot;
			memcpy(t[nq].nxt,t[q].nxt,sizeof t[q].nxt);
			t[nq].fa=t[q].fa,t[nq].len=t[p].len+1;
			t[q].fa=t[np].fa=nq;
			while(p&&t[p].nxt[c]==q)t[p].nxt[c]=nq,p=t[p].fa;
		}
	}
}
inline void solve(int val)
{
	int l=getint(),r=getint();
	r=r-l+1,l=Id[l];
	for(int j=19;~j;j--)if(f[l][j]&&t[f[l][j]].len>=r)l=f[l][j];
	pos[++cur]=val,len[cur]=r,g[l].push_back(cur);
}
inline void newnode(int u,int v)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,In[v]++;}
inline void toposort()
{
	queue<int>Q;
	for(int i=1;i<=cur;i++){F[i]=len[i];if(!In[i])Q.push(i);}
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=fir[u];i;i=nxt[i])
		{
			F[to[i]]=max(F[to[i]],F[u]+len[to[i]]);
			if(!(--In[to[i]]))Q.push(to[i]);
		}
	}
}

int main()
{
	int T=getint();
	while(T--)
	{
		lst=tot=1;
		memset(t,0,sizeof t);
		memset(fir,0,sizeof fir),cnt=0;
		memset(In,0,sizeof In),memset(pos,0,sizeof pos);
		scanf("%s",s+1);n=strlen(s+1);
		for(int i=n;i;i--)insert(s[i]-'a'),Id[i]=lst;
		for(int i=1;i<=tot;i++)f[i][0]=t[i].fa;
		for(int j=1;j<20;j++)for(int i=1;i<=tot;i++)f[i][j]=f[f[i][j-1]][j-1];
		na=getint();cur=tot;
		for(int i=1;i<=na;i++)solve(1),A[i]=cur;
		nb=getint();
		for(int i=1;i<=nb;i++)solve(0),B[i]=cur;
		for(int i=1;i<=tot;i++)
		{
			sort(g[i].begin(),g[i].end(),cmp);
			int pre=i;
			for(int j=0;j<g[i].size();j++)
			{
				newnode(pre,g[i][j]);
				if(!pos[g[i][j]])pre=g[i][j];
			}
			Ed[i]=pre;
		}
		for(int i=1;i<=tot;i++)g[i].clear();
		for(int i=2;i<=tot;i++)newnode(Ed[t[i].fa],i);
		for(int i=1;i<=cur;i++)if(!pos[i])len[i]=0;
		m=getint();
		for(int i=1;i<=m;i++)
		{
			int u=getint(),v=getint();
			newnode(A[u],B[v]);
		}
		toposort();
		bool flg=0;
		for(int i=1;i<=cur;i++)if(In[i]){flg=1;break;}
		if(flg)printf("-1\n");
		else printf("%lld\n",*max_element(F+1,F+cur+1));
	}
}

posted @ 2020-06-11 19:48  Izayoi_Doyo  阅读(189)  评论(0编辑  收藏  举报