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
#include
#include
#include
#include
#include
#include

#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];
vectorg[maxn<<2];
inline bool cmp(int x,int y){return len[x]==len[y]?pos[x]=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()
{
	queueQ;
	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

你可能感兴趣的:(LOJ3049 「十二省联考 2019」字符串问题)