zoukankan      html  css  js  c++  java
  • [BZOJ3879]SvT(后缀树+虚树)

    [BZOJ3879]SvT(后缀树+虚树)

    题面

    有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].
    现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP的长度之和.一对后缀之间的LCP长度仅统计一遍.

    分析

    建出S的后缀树(实现上直接建反串SAM的parent树). 每一组询问的后缀相当于后缀树上的一个点集。两个后缀在后缀树上对应的节点LCA的len就是他们的LCP长度。因此我们要查询这些点两两之间LCA的len之和。

    把这些关键点建出虚树。然后在虚树上做类似树形dp的统计。设(sz_x)表示(x)当前已经合并的子树中关键点的个数,则对于还未被合并的儿子(y),它和已经合并的子树会产生(sz_x cdot sz_y)对点,而这些点对的LCA都是(x)。因此对答案的贡献为(sz_x cdot sz_y cdot len(x))

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    #define maxc 26
    #define maxn 3000000
    #define maxm 3000000
    #define mod 23333333333333333ll
    using namespace std;
    template<typename T> void qread(T &x){
    	x=0;
    	T sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9'){
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9'){
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign; 
    } 
    template<typename T> void qprint(T x){
    	if(x<0){
    		putchar('-');
    		qprint(-x);
    	}else if(x==0){
    		putchar('0');
    		return;
    	}else{
    		if(x>=10) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    
    int n,m;
    char str[maxn+5];
    int pos[maxn+5];
    vector<int>E[maxn+5];
    
    int qu,q[maxm+5];
    
    struct SAM {
    #define len(x) (t[x].len)
    #define link(x) (t[x].link)
    	struct node {
    		int ch[maxc];
    		int link;
    		int len;
    	} t[maxn+5];
    	const int root=1;
    	int ptr=1;
    	int last=root;
    	int extend(int c) {
    		int p=last,cur=++ptr;
    		len(cur)=len(p)+1;
    		while(p&&t[p].ch[c]==0) {
    			t[p].ch[c]=cur;
    			p=link(p);
    		}
    		if(p==0) link(cur)=root;
    		else {
    			int q=t[p].ch[c];
    			if(len(p)+1==len(q)) link(cur)=q;
    			else {
    				int clo=++ptr;
    				t[clo]=t[q];
    				len(clo)=len(p)+1;
    				link(q)=link(cur)=clo;
    				while(p&&t[p].ch[c]==q) {
    					t[p].ch[c]=clo;
    					p=link(p);
    				}
    			}
    		}
    		last=cur;
    		return cur;
    	}
    	void build(char *s){
    		int len=strlen(s+1);
    		for(int i=len;i>=1;i--) pos[i]=extend(s[i]-'a');
    		for(int i=1;i<=ptr;i++) E[link(i)].push_back(i);
    #ifdef DEBUG
    		puts("tree:");
    		for(int i=1;i<=ptr;i++) printf("%d %d
    ",link(i),i);
    		printf("pos:"); 
    		for(int i=1;i<=len;i++) printf("%d ",pos[i]);
    		printf("
    ");
    #endif
    	}
    }S;
    
    int tim;
    int dfn[maxm+5],deep[maxm+5],top[maxm+5],son[maxm+5],fa[maxm+5],sz[maxm+5];
    void dfs1(int x,int f){
    	sz[x]=1;
    	fa[x]=f;
    	deep[x]=deep[f]+1;
    	for(int i=0;i<(int)E[x].size();i++){
    		int y=E[x][i];
    		if(y!=f){
    			dfs1(y,x);
    			sz[x]+=sz[y];
    			if(sz[y]>sz[son[x]]) son[x]=y;
    		}
    	}
    }
    void dfs2(int x,int t){
    	dfn[x]=++tim;
    	top[x]=t;
    	if(son[x]) dfs2(son[x],t);
    	for(int i=0;i<(int)E[x].size();i++){
    		int y=E[x][i];
    		if(y!=fa[x]&&y!=son[x]) dfs2(y,y); 
    	}
    }
    int lca(int x,int y){
    	while(top[x]!=top[y]){
    		if(deep[top[x]]<deep[top[y]]) swap(x,y);
    		x=fa[top[x]];
    	}
    	return deep[x]<deep[y]?x:y;
    }
    
    vector<int>vt[maxm+5];//虚树
    bool is_orig[maxm+5];//虚树中的关键点(不含lca) 
    int stk[maxm+5],_top; 
    inline int cmp(int x,int y){
    	return dfn[x]<dfn[y];
    }
    void vt_insert(int x){
    	if(_top<1){
    		stk[++_top]=x;
    		return;
    	}
    	int lc=lca(x,stk[_top]);
    	if(stk[_top]==lc){
    		stk[++_top]=x;
    		return;
    	}
    	while(_top>1&&deep[stk[_top-1]]>=deep[lc]){
    		vt[stk[_top-1]].push_back(stk[_top]);
    		_top--;
    	}
    	if(stk[_top]!=lc) vt[lc].push_back(stk[_top]);
    	stk[_top]=lc;
    	stk[++_top]=x;
    } 
    long long ans=0;
    int dfs3(int x,int f){
    	int szx=0;//子树中关键点个数,只开一个变量,这样不用清空
    	if(is_orig[x]) szx++; 
    	for(int i=0;i<(int)vt[x].size();i++){
    		int y=vt[x][i];
    		if(y!=f){
    			int szy=dfs3(y,x);
    			ans=(ans+1ll*szx*szy*S.t[x].len%mod)%mod;
    			szx+=szy;
    		}
    		
    	}
    	vt[x].clear(); 
    	return szx;
    }
    void get_tree(int *p,int cnt){
    	sort(p+1,p+1+cnt,cmp);
    	cnt=unique(p+1,p+1+cnt)-p-1;//题目说可能有重复后缀 
    	_top=0;
    	for(int i=1;i<=cnt;i++) is_orig[p[i]]=1; 
    	int root=0;
    	for(int i=1;i<=cnt;i++){
    		if(!root) root=p[i];
    		else root=lca(root,p[i]);
    	}
    	for(int i=1;i<=cnt;i++) vt_insert(p[i]);
    	while(_top>1){
    		vt[stk[_top-1]].push_back(stk[_top]);
    		_top--;
    	}
    	ans=0;
    	dfs3(root,0);
    	for(int i=1;i<=cnt;i++) is_orig[p[i]]=0; 
    }
    int main(){
    	qread(n);
    	qread(m);
    	scanf("%s",str+1);
    	S.build(str);
    	dfs1(1,0);
    	dfs2(1,1); 
    	for(int i=1;i<=m;i++){
    		qread(qu);
    		for(int j=1;j<=qu;j++){
    			qread(q[j]);
    			q[j]=pos[q[j]];
    		}
    		get_tree(q,qu);
    		qprint(ans);
    		putchar('
    ');
    	}
    }
    /*
    4 3
    aaaa
    2 3 4
    */ 
    
  • 相关阅读:
    Codeforces 1045C Hyperspace Highways (看题解) 圆方树
    Codeforces 316E3 线段树 + 斐波那切数列 (看题解)
    Codeforces 803G Periodic RMQ Problem 线段树
    Codeforces 420D Cup Trick 平衡树
    Codeforces 295E Yaroslav and Points 线段树
    Codeforces 196E Opening Portals MST (看题解)
    Codeforces 653F Paper task SA
    Codeforces 542A Place Your Ad Here
    python基础 异常与返回
    mongodb 删除
  • 原文地址:https://www.cnblogs.com/birchtree/p/12594319.html
Copyright © 2011-2022 走看看