• [BZOJ2555]SubString


    VII.[BZOJ2555]SubString

    如果要在动态建SAM的过程中同时维护parent tree中的子树和,明显需要一种支持修改树的数据结构来维护。显然,这里应该使用LCT。

    维护子树和,可以考虑LCT中经典的记录虚子树和的trick。然后剩下就是俩模板的拼接了。

    不知道为什么,交上去会MLE(尽管空间经计算是不会爆炸的),但是与正解对拍是OK的(

    MLE代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=600100;
    
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    struct Link_Cut_Tree{int ch[2],fa,sumi,sum;bool rev;}t[N<<1];
    void REV(int x){t[x].rev^=1,swap(lson,rson);}
    void ADD(int x){t[x].sumi++,t[x].sum++;}
    void pushup(int x){t[x].sum=t[x].sumi;if(lson)t[x].sum+=t[lson].sum;if(rson)t[x].sum+=t[rson].sum;}
    void pushdown(int x){if(!t[x].rev)return;if(lson)REV(lson);if(rson)REV(rson);t[x].rev=false;}
    int identify(int x){if(x==t[t[x].fa].ch[0])return 0;if(x==t[t[x].fa].ch[1])return 1;return -1;}
    void rotate(int x){
    	register int y=t[x].fa,z=t[y].fa,dirx=identify(x),diry=identify(y),b=t[x].ch[!dirx];
    	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
    	if(b)t[b].fa=y;t[y].ch[dirx]=b;
    	t[y].fa=x,t[x].ch[!dirx]=y;
    	pushup(y),pushup(x);
    }
    void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
    void splay(int x){for(pushall(x);identify(x)!=-1;rotate(x))if(identify(t[x].fa)!=-1)rotate(identify(x)==identify(t[x].fa)?t[x].fa:x);}
    void access(int x){for(register int y=0;x;x=t[y=x].fa)splay(x),t[x].sumi+=t[rson].sum-t[y].sum,rson=y,pushup(x);}
    void makeroot(int x){access(x),splay(x),REV(x);}
    void split(int x,int y){makeroot(x),access(y),splay(y);}
    bool link(int x,int y){split(x,y),t[x].fa=y,t[y].sumi+=t[x].sum,pushup(y);}
    void cut(int x,int y){split(x,y),t[y].ch[0]=t[x].fa=0,pushup(y);}
    
    struct Suffix_Automaton{int fa,len,ch[2];}sam[N<<1];
    int cnt=1,las=1;
    void Setfa(int x,int y){//set fa[x] to y.
    	if(sam[x].fa)cut(x,sam[x].fa);
    	sam[x].fa=y,link(x,y);
    }
    int Add(int x,int c){
    	int xx=++cnt;sam[xx].len=sam[x].len+1;
    	for(;x&&!sam[x].ch[c];x=sam[x].fa)sam[x].ch[c]=xx;
    	if(!x){Setfa(xx,1);return xx;}
    	int y=sam[x].ch[c];
    	if(sam[y].len==sam[x].len+1){Setfa(xx,y);return xx;}
    	int yy=++cnt;
    	for(int i=0;i<2;i++)sam[yy].ch[i]=sam[y].ch[i];Setfa(yy,sam[y].fa),sam[yy].len=sam[x].len+1;
    	Setfa(xx,yy),Setfa(y,yy);
    	for(;x&&sam[x].ch[c]==y;x=sam[x].fa)sam[x].ch[c]=yy;
    	return xx;
    }
    
    char s[N];
    int S;
    void Insert(){for(int i=0;i<S;i++)las=Add(las,s[i]-'A'),split(1,las),ADD(las);}
    int Query(){
    	int x=1;
    	for(int i=0;i<S;i++)if(sam[x].ch[s[i]-'A'])x=sam[x].ch[s[i]-'A'];else return 0;
    	split(x,sam[x].fa);
    	return t[x].sum;
    }
    
    int q;
    void deco(int msk){scanf("%s",s),S=strlen(s);for(int i=0;i<S;i++)msk=(msk*131+i)%S,swap(s[i],s[msk]);}
    int msk;
    char ord[10];
    int main(){
    	scanf("%d",&q);
    	scanf("%s",s),S=strlen(s),Insert();
    	for(int tp;q--;){
    		scanf("%s",ord),tp=(ord[0]=='A'?0:1);
    		deco(msk);
    		if(tp)printf("%d\n",tp=Query()),msk^=tp;
    		else Insert();
    	}
    	return 0;
    }
    

    下面我们考虑另一种思路,即把子树求和,单点加变为路径加,单点求值。路径加是LCT经典操作。

    但是,我们考虑在建parent tree的过程中我们有哪些操作:

    1. 为一个节点新增儿子

    明显此时该儿子的答案是 \(0\),故直接添加即可。

    1. 在一条边中塞一个新节点

    这时候,新添加的节点的答案就不是 \(0\) 了——其等于路径中深度较深的那个点当前的答案。于是我们在塞新节点的时候还要手动为它赋初值。

    写带 linkcut 的LCT是可以的。但是,我们这里可以使用定根LCT,即不使用 makeroot 的LCT。具体而言,显然情形1就直接连父亲即可,而情形2,假设我们往边 \((x,z)\) 中插一个节点 \(y\),则我们先 access(z)splay(x),这下子 \(x\) 的右儿子就一定是 \(z\),此时就可以直接在中间插一个 \(y\) 即可。明显此种做法常数更小。

    代码(可以通过):

    #include<bits/stdc++.h>
    using namespace std;
    const int N=600100;
    
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    struct Link_Cut_Tree{int ch[2],fa,val,tag;}t[N<<1];
    void ADD(int x,int y=1){t[x].val+=y,t[x].tag+=y;}
    void pushdown(int x){if(lson)ADD(lson,t[x].tag);if(rson)ADD(rson,t[x].tag);t[x].tag=0;}
    int identify(int x){if(x==t[t[x].fa].ch[0])return 0;if(x==t[t[x].fa].ch[1])return 1;return -1;}
    void rotate(int x){
    	register int y=t[x].fa,z=t[y].fa,dirx=identify(x),diry=identify(y),b=t[x].ch[!dirx];
    	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
    	if(b)t[b].fa=y;t[y].ch[dirx]=b;
    	t[y].fa=x,t[x].ch[!dirx]=y;
    }
    void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
    void splay(int x){for(pushall(x);identify(x)!=-1;rotate(x))if(identify(t[x].fa)!=-1)rotate(identify(x)==identify(t[x].fa)?t[x].fa:x);}
    void access(int x){for(register int y=0;x;x=t[y=x].fa)splay(x),rson=y;}
    void linkson(int x,int y){t[y].fa=x;}//make y a son of x
    void iterate(int x){
    	if(!x)return;
    	iterate(lson);
    	printf("%d ",x);
    	iterate(rson);
    }
    void expandedge(int x,int y,int z){//insert y between x and z
    	access(z),splay(x);//after this step, x's rson must be z.
    	t[y].fa=x,t[x].ch[1]=y;
    	t[z].fa=y,t[y].ch[1]=z;//insert y between x and z
    	t[y].val=t[z].val;
    }
    
    struct Suffix_Automaton{int fa,len,ch[2];}sam[N<<1];
    int cnt=1,las=1;
    int Add(int x,int c){
    	int xx=++cnt;sam[xx].len=sam[x].len+1;
    	for(;x&&!sam[x].ch[c];x=sam[x].fa)sam[x].ch[c]=xx;
    	if(!x){sam[xx].fa=1,linkson(1,xx);return xx;}
    	int y=sam[x].ch[c];
    	if(sam[y].len==sam[x].len+1){sam[xx].fa=y,linkson(y,xx);return xx;}
    	int yy=++cnt;sam[yy]=sam[y],sam[yy].len=sam[x].len+1;
    	sam[xx].fa=sam[y].fa=yy;
    	expandedge(sam[yy].fa,yy,y),linkson(yy,xx);
    	for(;x&&sam[x].ch[c]==y;x=sam[x].fa)sam[x].ch[c]=yy;
    	return xx;
    }
    
    char s[N];
    int S;
    void Insert(){for(int i=0;i<S;i++)las=Add(las,s[i]-'A'),access(las),splay(las),ADD(las);}
    int Query(){
    	int x=1;
    	for(int i=0;i<S;i++)if(sam[x].ch[s[i]-'A'])x=sam[x].ch[s[i]-'A'];else return 0;
    	splay(x);
    	return t[x].val;
    }
    
    int q;
    void deco(int msk){scanf("%s",s),S=strlen(s);for(int i=0;i<S;i++)msk=(msk*131+i)%S,swap(s[i],s[msk]);}
    int msk;
    char ord[10];
    int main(){
    	scanf("%d",&q);
    	scanf("%s",s),S=strlen(s),Insert();
    	for(int tp;q--;){
    		scanf("%s",ord),tp=(ord[0]=='A'?0:1);
    		deco(msk);
    		if(tp)printf("%d\n",tp=Query()),msk^=tp;
    		else Insert();
    	}
    	return 0;
    }
    

  • 相关阅读:
    Python图形编程探索系列-07-程序登录界面设计
    英语初级学习系列-05-阶段1总结
    Python图形编程探索系列-06-按钮批量生产函数
    英语初级学习系列-04-年龄
    Python图形编程探索系列-05-用控制变量构建对话程序
    Python图形编程探索系列-04-网上图片与标签组件的结合
    Python图形编程探索系列-03-标签组件(Label)
    Python解释数学系列——分位数Quantile
    Python图形编程探索系列-02-框架设计
    Python图形编程探索系列-01-初级任务
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605693.html
Copyright © 2020-2023  润新知