P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)

P2336 [SCOI2012]喵星球上的点名

名字怎么存?显然是后缀自动机辣

询问点到多少个喵喵喵其实就是

查询后缀自动机上parent树的一个子树

于是我们考虑莫队

怎么树上莫队呢

我们用dfs序处理后缀自动机上的parent树

把parent树映射到序列上

于是我们就可以愉快地莫队辣

最后怎么处理每个喵喵喵被点到的次数呢

我们在莫队的时候维护一个$Ls[i]$数组维护颜色$i$的存在时间(显然会被划分为好几段)

当颜色$i$第一次进队(队内本来没有该颜色)时,就记下$Ls[i]$

当队中最后一个颜色$i$出队时,答案就累加上$now-Ls[i]$,即为颜色$i$在这段的存在时间

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define rint register int
using namespace std;
int read(){
    char c=getchar();int x=0;
    while(c<'0'||c>'9') c=getchar();
    while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar();
    return x;
}
#define N 200005
int n,m,Len,L,R,dfn[N],siz[N],Co[N];
int tt,in[N],Ls[N],ans1[N],ans2[N],tot;
int cnt,hd[N],nxt[N],ed[N],poi[N];
struct data{int l,r,Id;}a[N];
inline bool cmp(data A,data B){
    return (A.l/Len==B.l/Len)?A.r<B.r:A.l/Len<B.l/Len;
}
inline void adde(int x,int y){
    nxt[ed[x]]=++cnt, hd[x]=hd[x]?hd[x]:cnt,
    ed[x]=cnt, poi[cnt]=y;
}
struct Sam{
    int fa[N],len[N],col[N],ed,q,Clock;
    Sam(){Clock=0;ed=1;}
    map<int,int> mp[N];
    int add(int p,int x,int id){
        len[++ed]=len[p]+1; col[ed]=id;
        for(;p&&!mp[p][x];p=fa[p]) mp[p][x]=ed;
        if(!p) {fa[ed]=1;return ed;}
        q=mp[p][x];
        if(len[q]==len[p]+1){fa[ed]=q;return ed;}
        len[++ed]=len[p]+1; mp[ed]=mp[q];
        fa[ed]=fa[q]; fa[q]=fa[ed-1]=ed;
        for(;mp[p][x]==q;p=fa[p]) mp[p][x]=ed;
        return ed-1;
    }
    void link(){for(rint i=1;i<=ed;++i) adde(fa[i],i);}
    void dfs(int x){//dfs序把parent树映射到序列上
        dfn[x]=++Clock; Co[Clock]=col[x]; siz[x]=1;
        for(int i=hd[x];i;i=nxt[i])
            dfs(poi[i]),siz[x]+=siz[poi[i]];
    }
}S;
int main(){
    n=read();m=read();int q,now;
    for(rint i=1;i<=n;++i){
        q=read(); now=1;
        while(q--) now=S.add(now,read(),i);
        q=read(); now=1;
        while(q--) now=S.add(now,read(),i);
    }S.link(); S.dfs(1); Len=sqrt(S.ed);
    for(rint i=1;i<=m;++i){
        q=read();now=1;
        while(q--) now=S.mp[now][read()];
        if(!now) continue;
        a[++tt]=(data){dfn[now],dfn[now]+siz[now]-1,i};
    }
    if(tt){//在映射序列上进行莫队
        sort(a+1,a+tt+1,cmp);
        for(rint i=a[1].l;i<=a[1].r;++i){
            if(!in[Co[i]]&&Co[i]) ++tot,Ls[Co[i]]=1;
            ++in[Co[i]];
        }ans1[a[1].Id]=tot; L=a[1].l; R=a[1].r;
        for(rint i=2;i<=tt;++i){
            while(L<a[i].l){
                --in[Co[L]];
                if(!in[Co[L]]&&Co[L]) --tot,ans2[Co[L]]+=i-Ls[Co[L]];
                ++L;
            }
            while(R>a[i].r){
                --in[Co[R]];
                if(!in[Co[R]]&&Co[R]) --tot,ans2[Co[R]]+=i-Ls[Co[R]];
                --R;
            }
            while(L>a[i].l){
                --L;
                if(!in[Co[L]]&&Co[L]) ++tot,Ls[Co[L]]=i;
                ++in[Co[L]];
            }
            while(R<a[i].r){
                ++R;
                if(!in[Co[R]]&&Co[R]) ++tot,Ls[Co[R]]=i;
                ++in[Co[R]];
            }
            ans1[a[i].Id]=tot;
        }
        while(L<=R){
            --in[Co[L]];
            if(!in[Co[L]]&&Co[L]) --tot,ans2[Co[L]]+=tt-Ls[Co[L]]+1;
            ++L;
        }//注意最后剩下的数据要算进去
    }
    for(rint i=1;i<=m;++i) printf("%d\n",ans1[i]);
    for(rint i=1;i<=n;++i) printf("%d ",ans2[i]);
    return 0;
}

 

posted @ 2019-04-15 22:28  kafuuchino  阅读(201)  评论(0编辑  收藏  举报