●BZOJ 3998 [TJOI2015]弦论

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=3998
题解:

后缀自动机。
当T=0时,
由于在后缀自动机上沿着trans转移,每个串都是互不相同的,
就只需要统计出从每个状态出发,存在多少条不同的路径,即有多少个不同的子串。
这个可以拓扑排序后用DP解决。
转移: $$cnt[p]=\sum_{trans[p][*]=q,q!=0} cnt[q] + 1$$
然后配合cnt[]去dfs即可。
当T=1时,
这时考虑了相同的子串。但是不难发现,
如果沿着trans转移到了一个状态s,
那么s的Right集合大小就表示了这个串出现了多少次。
所以沿用上面T=0的做法,只是把DP转移稍稍修改一下:
转移: $$cnt[p]=\sum_{trans[p][*]=q,q!=0} cnt[q] + right[p]$$
(把+1改为+right[p即可],接下来同样是配合cnt[]去dfs。)
(本人代码跑得很慢,2333)


代码:

 

#include<bits/stdc++.h>
#define MAXN 500005
#define ll long long
using namespace std;
ll cnt[MAXN*3];
struct SAM{
	int size;
	int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3],right[MAXN*3];
	int Newnode(int a,int b){
		++size; maxs[size]=a;
		memcpy(trans[size],trans[b],sizeof(trans[b]));
		return size;
	}
	int Extend(int last,int x){
		static int p,np,q,nq;
		p=last; np=Newnode(maxs[p]+1,0);
		for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np;
		if(!p) parent[np]=1;
		else{
			q=trans[p][x];
			if(maxs[p]+1!=maxs[q]){
				nq=Newnode(maxs[p]+1,q);
				parent[nq]=parent[q];
				parent[q]=parent[np]=nq;
				for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq;
			}
			else parent[np]=q;
		}
		return np;
	}
	void Build(char *S){
		static int p=1,last,tmp[MAXN],order[MAXN*3],len;
		memset(trans[0],0,sizeof(trans[0]));
		size=0; last=Newnode(0,0); len=strlen(S);
		for(int i=0;i<len;i++) last=Extend(last,S[i]-'a');
		
		for(int i=0;i<len;i++) p=trans[p][S[i]-'a'],right[p]++;
		for(int i=1;i<=size;i++) tmp[maxs[i]]++;
		for(int i=1;i<=len;i++) tmp[i]+=tmp[i-1];
		for(int i=1;i<=size;i++) order[tmp[maxs[i]]--]=i;
		for(int i=size;i;i--) p=order[i],right[parent[p]]+=right[p];
	}
	void Count(int t){
		static queue<int> Q;
		static int in[MAXN*3],order[MAXN*3],ont;
		for(int p=1;p<=size;p++)
			for(int c=0;c<26;c++) if(trans[p][c])
				in[trans[p][c]]++;
		Q.push(1);
		while(!Q.empty()){
			int u=Q.front(); Q.pop(); order[++ont]=u;
			for(int c=0;c<26;c++) if(trans[u][c]){
				in[trans[u][c]]--;
				if(!in[trans[u][c]]) Q.push(trans[u][c]);
			}
		}
		for(int i=size,p;i;i--){
			p=order[i]; cnt[p]=p==1?0:t==0?1:right[p];
			for(int c=0;c<26;c++) if(trans[p][c])
				cnt[p]+=cnt[trans[p][c]];
		}
	}
}SUF;
void DFS(int p,int k,int t,int from){
	static int i;
	static char ans[MAXN];
	if(p==1) i=0;
	else{
		ans[i++]=from+'a';
		k-=t==0?1:SUF.right[p];
	}
	if(k<=0) return (void)(ans[i]=0,puts(ans));
	for(int c=0;c<26;c++){
		if(k<=cnt[SUF.trans[p][c]]){
			DFS(SUF.trans[p][c],k,t,c);
			break;
		}
		k-=cnt[SUF.trans[p][c]];
	}
}
int main(){
	int T,K;
	static char S[MAXN];
	scanf("%s%d%d",S,&T,&K);
	SUF.Build(S);
	SUF.Count(T);
//	printf("%lld\n",cnt[1]);
	if(K<=cnt[1]) DFS(1,K,T,0);
	else puts("-1");
	return 0;
}

 

  

 

posted @ 2018-03-10 20:31  *ZJ  阅读(150)  评论(0编辑  收藏  举报