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