【BZOJ2555】SubString(SAM)(LCT)

传送门


题解:

仔细想一想发现我们需要维护 r i g h t right right集合的大小,也就是 f a i l fail fail树的子树大小

由于强制在线,随便用LCT维护一下子树 s i z siz siz即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int get_s(char *s){
		int len=0;char c;
		while(isspace(c=gc()));
		while(s[len++]=c,!isspace(c=gc())&&c!=EOF);
		return s[len]='\0',len;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=3e6+7;

int m;
char s[N];int sl;

int msk;
inline void decode(char *s,int l){
	for(int re i=0,tp=msk;i<l;++i)
	std::swap(s[i],s[tp=(tp*131+i)%l]);
}

namespace LCT{
	cs int N=3e6+7;
	int son[N][2],fa[N],sum[N],sub[N],val[N];
	inline void pushup(int u){sum[u]=sum[son[u][0]]+sum[son[u][1]]+sub[u]+val[u];}
	inline bool which(int u){return son[fa[u]][1]==u;}
	inline bool isrt(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
	inline void Rotate(int u){
		int p=fa[u],pp=fa[p],d=which(u);
		if(!isrt(p))son[pp][which(p)]=u;
		fa[u]=pp,fa[p]=u,fa[son[u][!d]]=p;
		son[p][d]=son[u][!d],son[u][!d]=p;
		pushup(p),pushup(u);
	}
	inline void Splay(int u){
		for(int re p=fa[u];!isrt(u);Rotate(u),p=fa[u])
		if(!isrt(p))Rotate(which(p)==which(u)?p:u);
	}
	inline void access(int u){
		for(int re ch=0;u;u=fa[ch=u]){
			Splay(u);sub[u]+=sum[son[u][1]]-sum[ch];
			son[u][1]=ch;pushup(u);
		}
	}
	inline void link(int u,int f){access(f),Splay(f);fa[u]=f,sub[f]+=sum[u];pushup(f);}
	inline void cut(int u){access(u);Splay(u);fa[son[u][0]]=0,son[u][0]=0;pushup(u);}
	inline int qy(int u){access(u),Splay(u);return sub[u]+val[u];}
}

namespace SAM{
	cs int N=3e6+7;
	int son[N][26],len[N],fa[N],now=1,last=1;
	inline void push_back(int c){
		int cur=++now,p=last;last=cur;
		len[cur]=len[p]+1;LCT::sum[cur]=LCT::val[cur]=1;
		for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
		if(!p)LCT::link(cur,fa[cur]=1);
		else if(len[son[p][c]]==len[p]+1)LCT::link(cur,fa[cur]=son[p][c]);
		else {
			int nq=++now,q=son[p][c];
			memcpy(son[nq],son[q],sizeof son[q]);
			len[nq]=len[p]+1,LCT::link(nq,fa[nq]=fa[q]);
			LCT::cut(q),LCT::link(q,fa[q]=nq);LCT::link(cur,fa[cur]=nq);
			for(;p&&son[p][c]==q;p=fa[p])son[p][c]=nq;
		}
	}
	inline int query(){
		sl=get_s(s+1);decode(s+1,sl);int u=1;
		for(int re i=1;i<=sl&&u;++i)u=son[u][s[i]-'A'];
		if(!u)return 0;return LCT::qy(u);
	}
}

signed main(){
#ifdef zxyoi
	freopen("substring.in","r",stdin);
#endif
	m=gi();sl=get_s(s+1);
	for(int re i=1;i<=sl;++i)SAM::push_back(s[i]-'A');
	while(m--){
		get_s(s);
		switch(s[0]){
			case 'A':{
				sl=get_s(s+1);decode(s+1,sl);
				for(int re i=1;i<=sl;++i)SAM::push_back(s[i]-'A');
				break;
			}
			case 'Q':{
				int ans=SAM::query();
				cout<<ans<<"\n";
				msk^=ans;
				break;
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值