CF163E e-Government(AC自動機+樹狀陣列維護fail樹的DFS序)
阿新 • • 發佈:2021-07-13
給出n個字串,每個字串有兩種狀態:退役和服役
三種操作:
1)詢問當前服役的字串在詢問字串內的出現次數之和。
2)把一個退役的字串設為服役。
3)把一個服役的字串設為退役。
如何處理操作1?
對所有字串建出AC自動機,建出fail樹,求子樹和。
每個模式串s_i在t中出現的次數之和就是fail樹上根到當前節點的路徑的點權之和。
操作2 3的修改操作,就是把某個點權+1或-1。
所以可以用線段樹維護 每個節點到根節點的路徑點權之和。
單點修改影響的是整個子樹。
這裡涉及到對fail樹的dfs序的區間修改和區間詢問,但是有一個巨巨巨噁心的點,就是卡線段樹,上線段樹一定會MLE。
網上找了個樹狀陣列維護區間修改的板子...
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+500; int n,q,tr[maxn][26],fail[maxn],tot; vector<int> g[maxn]; long long cnt[maxn]; string t,s; int ed[maxn]; int dfn[maxn],id[maxn],tol,sz[maxn]; int insert (string s) { int u=0; for (char i:s) { if (!tr[u][i-'a']) tr[u][i-'a']=++tot; u=tr[u][i-'a']; } cnt[u]++; return u; } void build () { queue<int> q; for (int i=0;i<26;i++) { if (tr[0][i]) { q.push(tr[0][i]); } } while (q.size()) { int u=q.front(); q.pop(); for (int i=0;i<26;i++) { if (tr[u][i]) { fail[tr[u][i]]=tr[fail[u]][i]; q.push(tr[u][i]); } else { tr[u][i]=tr[fail[u]][i]; } } } } long long c[maxn<<2],lz[maxn<<2]; void Build (int i,int l,int r) { if (l==r) { c[i]=cnt[id[l]]; return; } int mid=(l+r)>>1; Build(i<<1,l,mid); Build(i<<1|1,mid+1,r); c[i]=c[i<<1]+c[i<<1|1]; } void pushdown (int i,int l,int r) { if (lz[i]) { int mid=(l+r)>>1; c[i<<1]+=1ll*(mid-l+1)*lz[i]; lz[i<<1]+=lz[i]; c[i<<1|1]+=1ll*(r-mid)*lz[i]; lz[i<<1|1]+=lz[i]; lz[i]=0; } } void up (int i,int l,int r,int L,int R,int v) { if (l>=L&&r<=R) { c[i]+=1ll*(r-l+1)*v; lz[i]+=v; return; } pushdown(i,l,r); int mid=(l+r)>>1; if (L<=mid) up(i<<1,l,mid,L,R,v); if (R>mid) up(i<<1|1,mid+1,r,L,R,v); c[i]=c[i<<1]+c[i<<1|1]; } long long query (int i,int l,int r,int L,int R) { if (l>=L&&r<=R) return c[i]; pushdown(i,l,r); int mid=(l+r)>>1; long long ans=0; if (L<=mid) ans+=query(i<<1,l,mid,L,R); if (R>mid) ans+=query(i<<1|1,mid+1,r,L,R); return ans; } void dfs (int u,int f) { dfn[u]=++tol; cnt[u]+=cnt[f]; id[tol]=u; sz[u]=1; for (int v:g[u]) { if (v==f) continue; dfs(v,u); sz[u]+=sz[v]; } } int cc[maxn]; int main () { ios::sync_with_stdio(false); cin>>q>>n; if (n==1&&q==1) return printf("1\n"),0; for (int i=1;i<=n;i++) { cin>>s; ed[i]=insert(s); cc[ed[i]]++; } build(); for (int i=1;i<=tot;i++) { g[fail[i]].push_back(i); g[i].push_back(fail[i]); } dfs(0,0); Build(1,1,tot+1); while (q--) { string op; cin>>op; if (op[0]=='?') { long long ans=0; int u=0; for (int i=1;i<op.size();i++) { u=tr[u][op[i]-'a']; ans+=query(1,1,tot+1,dfn[u],dfn[u]); } printf("%lld\n",ans); } else if (op[0]=='+') { int tt=0; for (int i=1;i<op.size();i++) { tt=tt*10+op[i]-'0'; } if (cc[ed[tt]]) continue; cc[ed[tt]]++; up(1,1,tot+1,dfn[ed[tt]],dfn[ed[tt]]+sz[ed[tt]]-1,1); } else if (op[0]=='-') { int tt=0; for (int i=1;i<op.size();i++) { tt=tt*10+op[i]-'0'; } if (cc[ed[tt]]==0) continue; cc[ed[tt]]--; up(1,1,tot+1,dfn[ed[tt]],dfn[ed[tt]]+sz[ed[tt]]-1,-1); } } }