【题解】[SCOI2012]喵星球上的点名

[SCOI2012]喵星球上的点名

\(\text{Solution:}\)

第一问很简单,直接树状数组实现子树数颜色就好了。问题在第二问。

第二问的感觉不是很好,无从下手。第一个想法是考虑是不是可以用线段树合并来做这件事,但感觉会很麻烦,并没有想清楚怎么做。

参考题解区的做法也没有详细讲解,这里根据自己的理解讲一下正确性,代码复杂度还有待商榷。据说期望是根号的。

考虑把所有串拎出来一个个求答案。从开头到结尾依次找到其在 parent 树上对应的节点,然后从这个节点不断向上跳父亲。

把沿途点的所有答案都累加上。这为什么是对的?

我们发现,parent 树上的父亲代表的串必然是其孩子的后缀。也就是说,如果我们找到了一个串的前缀对应的节点,那么这个节点在 parent 树上一直到根的答案都是需要累积的。

而且这样做显然是不会重复的。因为每一个记录的串结尾不同以及长度不同。

为了保证复杂度,每次直接对找过的点打标记。因为这个点向上的所有点其实都已经被计算过了。

于是依次遍历串去更新答案即可。复杂度证明暂时没有找到。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,tr[N];
inline int lowbit(int x){return x&(-x);}
void change(int x,int v,int TT){while(x<=TT)tr[x]+=v,x+=lowbit(x);}
int query(int x){
    if(!x)return 0;
    int res=0;
    while(x){
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}
namespace SAM{
    int len[N],pa[N],tot=1,last=1,col[N],siz[N];
    int dfstime,idfn[N],dfn[N],cpos[N],num[N];
    int pos[N],cdfn[N],cnt[N],Ansnum,Ans[N];
    map<int,int>ch[N];
    vector<int>G[N],qr[N];
    void insert(const int &c,const int &cl){
        int p=last;int np=++tot;last=tot;
        col[np]=cl;
        for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
        if(!p)pa[np]=1;
        else{
            int q=ch[p][c];
            if(len[q]==len[p]+1)pa[np]=q;
            else{
                int nq=++tot;
                len[nq]=len[p]+1;
                pa[nq]=pa[q];pa[q]=pa[np]=nq;
                ch[nq]=ch[q];
                for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
            }
        }
    }
    void dfs(int x){
        idfn[dfn[x]=++dfstime]=x;
        cdfn[dfstime]=col[x];
        siz[x]=1;
        for(auto v:G[x]){
            dfs(v);
            siz[x]+=siz[v];
        }
    }
    void Build(){
        for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
        dfs(1);
        for(int i=1;i<=tot;++i)
            qr[dfn[i]+siz[i]-1].push_back(i);
        for(int i=1;i<=tot;++i){
            if(!cpos[cdfn[i]])cpos[cdfn[i]]=i;
            pos[i]=cpos[cdfn[i]];
            cpos[cdfn[i]]=i;
        }
        for(int i=2;i<=tot;++i){
            if(cdfn[i]){
                change(i,1,tot);
                if(pos[i]!=i)change(pos[i],-1,tot);
            }
            for(auto v:qr[i]){
                num[v]=query(i)-query(dfn[v]-1);
            }
        }
    }
    void dfsans(int x){
        Ansnum+=cnt[x];
        int numx=0;
        for(auto v:G[x]){
            numx++;
            dfsans(v);
        }
        if(!numx)Ans[col[x]]+=Ansnum;
        Ansnum-=cnt[x];
    }
}
using namespace SAM;
int scnt,t[N<<1];
int lens[N][2];
inline void update(int x,int y){
	for(;x&&cpos[x]!=y;x=pa[x]){
		cpos[x]=y;
		Ans[y]+=cnt[x];
	}
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        int lent;
        last=1;
        scanf("%d",&lent);
        lens[i][0]=lent;
        for(int j=1;j<=lent;++j){
            int x;
            scanf("%d",&x);
            t[++scnt]=x;
            insert(x,i);
        }
        last=1;
        scanf("%d",&lent);
        lens[i][1]=lent;
        for(int j=1;j<=lent;++j){
            int x;
            scanf("%d",&x);
            t[++scnt]=x;
            insert(x,i);
        }
    }
    Build();
    // for(int i=1;i<=tot;++i)printf("%d ",num[i]);
    // puts("");
    // for(int i=1;i<=tot;++i)printf("%d ",col[i]);
    // puts("");
    // for(int i=1;i<=tot;++i)printf("%d ",cdfn[i]);
    // puts("");
    // for(int i=1;i<=tot;++i)printf("%d ",col[idfn[i]]);
    // puts("");
    // for(int i=1;i<=tot;++i)printf("(%d %d)\n",cpos[cdfn[i]],pos[i]);
    for(int qq=1;qq<=m;++qq){
        int lent;
        scanf("%d",&lent);
        int now=1,fg=0;;
        for(int i=1;i<=lent;++i){
            int v;
            scanf("%d",&v);
            now=ch[now][v];
            if(!now)fg=1;
        }
        if(fg)puts("0");
        else printf("%d\n",num[now]),cnt[now]++;
    }
    int snum=0;
    for(int i=1;i<=tot;++i)cpos[i]=0;
    for(int i=1;i<=n;++i){
        int now=1;
        for(int j=1;j<=lens[i][0];++j)now=ch[now][t[++snum]],update(now,i);
        now=1;
        for(int j=1;j<=lens[i][1];++j)now=ch[now][t[++snum]],update(now,i);
    }
    for(int i=1;i<=n;++i)printf("%d ",Ans[i]);
    return 0;
}
posted @ 2021-08-31 21:19  Refined_heart  阅读(27)  评论(0编辑  收藏  举报