洛谷P5840 [COCI2015]Divljak (AC自动机+fail树上dfs序+树上差分线段树维护)
题目链接:https://www.luogu.com.cn/problem/P5840
解法:如题所示,不得不承认是一道非常好的题,就是有点麻烦qwq。首先考虑用S建AC自动机,然后对每一个点建fail[i]->i的dfs树,然后把每次添加的T串丢到AC自动机上跑。每次跑的时候记录T串每个位置在AC自动机里的位置。排序后(因为如果不排序的话,这些点就杂乱无章,就无法有效地进行树上差分,我们要的是每次T串丢进来看该点有没有使用过而已,所以如果你学过虚树会有所了解,用树上差分就是2个点+1,其LCA-1,为什么是-1而不是-2呢?因为我们需要记录该点有没有使用过,使用过的话就一直是1,最后维护的线段树其实就是查询的点包括其子树一共有几个点,因为根据fail指针的性质我们可以知道,一旦他儿子有匹配到,那他自己必有匹配。大概就是这样子吧,具体看代码,感觉啰里啰嗦自己还说不清。
AC代码:
#include<bits/stdc++.h> #pragma GCC optimize(2) #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=2e6+5; int tot,head[maxn]; struct E{ int to,next; }edge[maxn<<1]; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } class ac_auto{ public: const static int maxn=2e6+100; int tot,root; int tree[maxn][30]; int pos[maxn]; int fail[maxn];int ans[1005]; int newnode(){ for(int i=0;i<26;i++){ tree[tot][i]=0; } pos[tot]=0; return tot++; } void init(){ mem(ans,0);tot=0; root=newnode(); } void insert_(string s,int t){ int now = root; for(int i=0;i<s.length();i++){ int id=s[i]-'a'; if(!tree[now][id]){ tree[now][id]=newnode(); } now=tree[now][id]; } pos[t]=now; } void getFail(){ queue <int>q; for(int i=0;i<26;i++){ if(tree[0][i]){ fail[tree[0][i]] = 0; q.push(tree[0][i]); } } while(!q.empty()){ int now = q.front(); q.pop(); for(int i=0;i<26;i++){ if(tree[now][i]){ fail[tree[now][i]] = tree[fail[now]][i]; q.push(tree[now][i]); } else tree[now][i] = tree[fail[now]][i]; } } } }acam; int fa[maxn],dep[maxn],siz[maxn],son[maxn]; void dfs1(int u,int f){ fa[u]=f; dep[u]=dep[f]+1; siz[u]=1; int maxsize=-1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==f) continue; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>maxsize){ maxsize=siz[v]; son[u]=v; } } } int tim,dfn[maxn],top[maxn]; void dfs2(int u,int t){ dfn[u]=++tim; top[u]=t; if(!son[u]) return ; dfs2(son[u],t); for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } int LCA(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) std::swap(x,y); x=fa[top[x]]; } if(dep[x]>dep[y]) std::swap(x,y); return x; } class segment_tree{ public: struct node{ int l,r; int sum; }tree[10000010]; inline void build(int i,int l,int r){ tree[i].l=l;tree[i].r=r; if(l==r){ tree[i].sum=0; return ; } int mid=(l+r)>>1; build(i*2,l,mid); build(i*2+1,mid+1,r); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; } inline int search(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r) return tree[i].sum; if(tree[i].r<l || tree[i].l>r) return 0; int s=0; if(tree[i*2].r>=l) s+=search(i*2,l,r); if(tree[i*2+1].l<=r) s+=search(i*2+1,l,r); return s; } inline void update(int i,int dis,int k){ if(tree[i].l==tree[i].r){ tree[i].sum+=k; return ; } if(dis<=tree[i*2].r) update(i*2,dis,k); else update(i*2+1,dis,k); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; return ; } }st; bool cmp(int a,int b){ return dfn[a]<dfn[b]; } int a[maxn]; void solve1(string s){ int now=acam.root,tp=0; for(int i=0;i<s.length();i++){ now = acam.tree[now][s[i]-'a']; a[++tp]=now; } sort(a+1,a+tp+1,cmp); bool flag=false; rep(i,1,tp){ st.update(1,dfn[a[i]],1); if(flag){ st.update(1,dfn[LCA(a[i],a[i-1])],-1); }else{ flag=true; } } } int solve2(int x){ return st.search(1,1,dfn[acam.pos[x]]+siz[acam.pos[x]]-1)-st.search(1,1,dfn[acam.pos[x]]-1); } int main(){ int n;scanf("%d",&n);mem(head,-1); acam.init(); rep(i,1,n){ string s;cin>>s; acam.insert_(s,i); } acam.getFail(); rep(i,1,acam.tot-1){ add(acam.fail[i],i); } dfs1(0,acam.tot); dfs2(0,0); st.build(1,1,tim); int q;scanf("%d",&q); while(q--){ int op;scanf("%d",&op); if(op==1){ string s;cin>>s; solve1(s); }else{ int x;scanf("%d",&x); printf("%d\n",solve2(x)); } } }
前ICPC算法竞赛退役选手|现摸鱼ing