LGP7582题解

他说风雨中这点痛算什么,擦干泪不要怕,至少我们还有梦

首先弄明白一件事:区间 ACAM 可以看做是所有串的 ACAM 只保留区间串对节点贡献的信息,仍然可以使用所有串的 ACAM 的转移边。

于是我们可以使用 ACAM 套线段树,每颗线段树维护这个节点会受到区间哪些位置的串的贡献。

但是这这样有一个问题,修改操作不太容易实现。

那么我们还需要再套一层数据结构来解决这个修改。区间询问一般考虑线段树和分块。线段树的话修改操作会涉及过多的节点,所以我们选择分块。

设块长为 \(B\)。对每个块的 ACAM 维护两种类型的值:\(V[u]\) 表示不计算整体加 \(tag\) 的权值之和,\(c[u]\) 表示这个节点包含的串的个数。

对于整块,区间加操作就直接加,如果是赋值那就清空 \(V\) 然后令 \(tag\) 等于赋的值。

我们是很容易通过这两个数组来计算一个节点的权值的。对于整块我们可以这样做到 \(O(\sum|S|\frac{n}{B})\) 个询问操作。

散块的修改操作需要暴力进行,涉及到的 ACAM 节点个数是 \(O(\sum|s|)\) 级别的。不过可以知道需要修改的 ACAM 节点是 \(O(B)\) 个。所以修改操作是 \(O(mB)\) 个。

但是询问就比较麻烦了。前面提到可以将区间 ACAM 看做是所有串的 ACAM 只保留区间串对节点贡献的信息,所以对于每个块的 ACAM 都套上一颗线段树,维护哪些串贡献这个节点。

但是查询的时候只能暴力把这个集合弄出来一个一个加。不过由于加的是字符串的信息所以是 \(O(1)\) 维护的。

查询散块的复杂度为 \(O(\sum|S|B)\)

注意到前面的询问次数过多,线段树这种东西是不能胜任的,考虑对 dfn 序分块。

每次修改相当于子树加,也就是区间加。我们将 fail 树的 dfn 序分成块长为 \(T\) 的块。每次修改暴力加后缀,然后 \(O(B)\) 次操作结束后进行一个块的前缀和。

那么就是 \(O(\sum|S|\frac{n}{B}+\sum|S|B+mBT+\frac{\sum|S|m}{T})\)\(T\)\(\sqrt{\frac{\sum|S|}{B}}\) 可以得到 \(O(\sum|S|\frac{n}{B}+m\sqrt{\sum|S|B})\) 的复杂度。

\(B\)\(\sqrt{n}\) 即可得到 \(O(\sum|S|\sqrt{n}+m\sqrt[4]{n}\sqrt{\sum|S|})\) 的复杂度。

线段树合并的话,序列长度其实只有 \(\lfloor\sqrt{30000}\rfloor=173\),这玩意儿还不如去用 bitset。空间几乎是线性的,不会被卡。

补充:bitset 部分的时间和空间都是 \(O(\sum|S|\frac{\sqrt{n}}{w})\)

#include<cstring>
#include<cstdio>
#include<bitset>
#include<cmath>
typedef long long ll;
const int M=2e5+5;
int n,m,B,P,tot,a[M];char S[M],T[M],*s[M],*t[M];int lens[M],lent[M];
int sums,sumt;int L[M],R[M],pos[M];int opt[M],l[M],r[M],k[M];
namespace Solve{
	int dfc,V[M],c[M],l[M],r[M],pos[M],fail[M],trans[M][3];std::bitset<180>bit[M];
	int _s[M<<1],_tim[M<<1],_S[M<<1];int _pos[M<<1],_L[M<<1],_R[M<<1];
	int*ns=_s,*ntim=_tim,*nS=_S,*npos=_pos,*nL=_L,*nR=_R;
	int cnt,h[M];
	struct Edge{
		int v,nx;
	}e[M];
	inline void Add(const int&u,const int&v){
		e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
	}
	inline int Insert(int u,char*s,const int&v,const int&n){
		for(int i=0;i<n;++i){
			const int&c=s[i]-97;
			if(!trans[u][c])trans[u][c]=++tot;u=trans[u][c];
		}
		V[u]+=v;++c[u];return u;
	}
	struct Block{
		int m,rt,tag,T,a[190],tim[190],end[190];
		struct BLOCK{
			int n,B,T,*s,*tim,*S;int*pos,*L,*R;bool typ;
			inline void init(const int&N){
				s=ns;tim=ntim;S=nS;pos=npos;L=nL;R=nR;n=N;
				ns+=n+5;ntim+=n+5;nS+=n+5;npos+=n+5;nL+=n+5;nR+=n+5;
				for(int i=1;i<=n;++i){
					pos[i]=(i-1)/B+1;
					L[i]=(pos[i]-1)*B+1;R[i]=pos[i]*B;if(R[i]>n)R[i]=n;
				}
			}
			inline void Add(int x,const int&V){
				for(int i=x;i<=R[x];++i){
					if(tim[i]^T)tim[i]=T,s[i]=0;s[i]+=V;
				}
			}
			inline void update(){
				for(int i=1;i<=pos[n];++i){
					if(tim[R[(i-1)*B+1]]^T)tim[R[(i-1)*B+1]]=T,s[R[(i-1)*B+1]]=0;S[i]=S[i-1]+s[R[(i-1)*B+1]];
				}
				typ=false;
			}
			inline int Qry(const int&x){
				if(tim[x]^T)tim[x]=T,s[x]=0;return typ?0:S[pos[x]-1]+s[x];
			}
			inline void Clear(){
				++T;
			}
		}block;
		inline void Build(const int&L,const int&R){
			for(int i=L;i<=R;++i)a[i-L+1]=::a[i],bit[end[i-L+1]=Insert(rt,::s[i],a[i-L+1],lens[i])][i-L+1]=1;
		}
		inline void init(const int&n){
			block.B=ceil(sqrt(sumt/B));block.init(m);
			for(int i=1;i<=n;++i)block.Add(l[end[i]],a[i]),block.Add(r[end[i]]+1,-a[i]);block.update();
		}
		inline void Add(const int&V){
			tag+=V;
		}
		inline void Cover(const int&V){
			block.Clear();++T;tag=V;block.typ=true;
		}
		inline void Add(const int&V,const int&L,const int&R){
			for(int i=L;i<=R;++i){
				if(tim[i]^T)tim[i]=T,a[i]=0;
				block.Add(l[end[i]],V);block.Add(r[end[i]]+1,-V);a[i]+=V;
			}
			block.update();
		}
		inline void Cover(const int&V,const int&L,const int&R){
			for(int i=L;i<=R;++i){
				if(tim[i]^T)tim[i]=T,a[i]=0;
				block.Add(l[end[i]],V-a[i]-tag);block.Add(r[end[i]]+1,-(V-a[i]-tag));a[i]=V-tag;
			}
			block.update();
		}
		inline ll Qry(char*s,const int&n){
			int u(rt);ll ans(0);
			for(int i=0;i<n;++i){
				u=trans[u][s[i]-97];
				ans+=block.Qry(l[u])+1ll*tag*c[u];
			}
			return ans;
		}
		inline ll Qry(char*s,const int&n,const int&L,const int&R){
			int u(rt);ll ans(0);
			for(int i=0;i<n;++i){
				u=trans[u][s[i]-97];
				for(int i=bit[u]._Find_next(L-1);i<=R;i=bit[u]._Find_next(i)){
					if(tim[i]^T)tim[i]=T,a[i]=0;ans+=a[i]+tag;
				}
			}
			return ans;
		}
	}block[190];
	inline void DFS(const int&u){
		if(fail[u]){
			V[u]+=V[fail[u]];c[u]+=c[fail[u]];bit[u]|=bit[fail[u]];
			pos[u]=pos[fail[u]];
		}
		l[u]=++dfc;++block[pos[u]].m;for(int E=h[u];E;E=e[E].nx)DFS(e[E].v);r[u]=dfc;
	}
	inline void Build(){
		static int L,R,q[M];L=1;
		for(int u=1;u<=P;++u){
			for(int c=0;c<3;++c){
				if(trans[u][c])q[++R]=trans[u][c],fail[trans[u][c]]=u;
				else trans[u][c]=u;
			}
		}
		while(L<=R){
			const int&u=q[L++];
			for(int c=0;c<3;++c){
				if(trans[u][c])q[++R]=trans[u][c],fail[trans[u][c]]=trans[fail[u]][c];
				else trans[u][c]=trans[fail[u]][c];
			}
		}
		for(int u=1;u<=tot;++u)Add(fail[u],u);
		for(int u=1;u<=P;++u)DFS(u),dfc=0;
	}
}
signed main(){
	scanf("%d%d",&n,&m);s[0]=S;t[0]=T;B=sqrt(n);P=(n+B-1)/B;
	for(int i=1;i<=n;++i){
		scanf("%s%d",s[i]=s[i-1]+lens[i-1],a+i);lens[i]=strlen(s[i]);sums+=lens[i];
	}
	for(int i=1;i<=m;++i){
		scanf("%d%d%d",opt+i,l+i,r+i);t[i]=t[i-1]+lent[i-1];
		if(opt[i]==3)scanf("%s",t[i]),lent[i]=strlen(t[i]);else scanf("%d",k+i);sumt+=lent[i];
	}
	for(int i=1;i<=n;++i){
		pos[i]=(i-1)/B+1;L[i]=(pos[i]-1)*B+1;R[i]=pos[i]*B;if(R[i]>n)R[i]=n;
	}
	for(int i=1;i<=P;++i)Solve::block[i].rt=++tot,Solve::pos[i]=i;
	for(int i=1;i<=P;++i)Solve::block[i].Build(L[(i-1)*B+1],R[(i-1)*B+1]);Solve::Build();
	for(int i=1;i<=P;++i)Solve::block[i].init(R[(i-1)*B+1]-L[(i-1)*B+1]+1);
	for(int i=1;i<=m;++i){
		const int&l=::l[i],&r=::r[i],&k=::k[i];
		if(opt[i]==1){
			const int&q=pos[l],&p=pos[r];
			if(q==p)Solve::block[q].Add(k,l-L[l]+1,r-L[r]+1);
			else{
				Solve::block[q].Add(k,l-L[l]+1,R[l]-L[l]+1);Solve::block[p].Add(k,1,r-L[r]+1);
				for(int i=q+1;i<=p-1;++i)Solve::block[i].Add(k);
			}
		}
		if(opt[i]==2){
			const int&q=pos[l],&p=pos[r];
			if(q==p)Solve::block[q].Cover(k,l-L[l]+1,r-L[r]+1);
			else{
				Solve::block[q].Cover(k,l-L[l]+1,R[l]-L[l]+1);Solve::block[p].Cover(k,1,r-L[r]+1);
				for(int i=q+1;i<=p-1;++i)Solve::block[i].Cover(k);
			}
		}
		if(opt[i]==3){
			const int&q=pos[l],&p=pos[r];
			const int&len=lent[i];char*t=::t[i];
			if(q==p)printf("%lld\n",Solve::block[q].Qry(t,len,l-L[l]+1,r-L[l]+1));
			else{
				ll ans=Solve::block[q].Qry(t,len,l-L[l]+1,R[l]-L[l]+1)+Solve::block[p].Qry(t,len,1,r-L[r]+1);
				for(int i=q+1;i<=p-1;++i)ans+=Solve::block[i].Qry(t,len);
				printf("%lld\n",ans);
			}
		}
	}
}
posted @ 2022-03-28 09:53  Prean  阅读(101)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};