zoukankan      html  css  js  c++  java
  • ●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
    ",cnt[1]);
    	if(K<=cnt[1]) DFS(1,K,T,0);
    	else puts("-1");
    	return 0;
    }
    

      

  • 相关阅读:
    kvm克隆虚拟机
    vSAN添加license
    KVM虚拟机快照
    第二次作业及总结——数据类型和运算符
    第二次作业心得
    做完c语言作业的心得
    介绍自己
    awk数组 Jazz
    Java入门第一阶段总结
    区间dp入门+例题
  • 原文地址:https://www.cnblogs.com/zj75211/p/8541860.html
Copyright © 2011-2022 走看看