zoukankan      html  css  js  c++  java
  • 【题解】[SCOI2012]喵星球上的点名

    [SCOI2012]喵星球上的点名

    ( ext{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)
    ",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
    ",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;
    }
    
  • 相关阅读:
    P1032 字串变换
    P3203 [HNOI2010]弹飞绵羊
    P3690 【模板】Link Cut Tree (动态树)
    P2147 [SDOI2008]洞穴勘测
    P3950 部落冲突
    Codeforces Round #469 Div. 2题解
    线段树
    SDOI2018退役记
    4.1模拟题
    无旋Treap
  • 原文地址:https://www.cnblogs.com/h-lka/p/15212274.html
Copyright © 2011-2022 走看看