[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;
}

posted @ 2021-04-01 12:01  Troverld  阅读(61)  评论(0编辑  收藏  举报