zoukankan      html  css  js  c++  java
  • BZOJ 3238 [Ahoi2013]差异

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3238

    发现前面的len(Ti)+len(Tj)都是定值,所以其实我们的目标就是求出lcp(Ti,Tj)来.

    lcp说实话就是一段子串对吧,但是不太好找.所以我们可以把串反过来.

    这样的话,原图中后缀的最长公共前缀就变成了新串中前缀的最长公共后缀.

    这样的话提到了两个概念:一是前缀,一是最长公共前缀

    前缀的话就是每个状态的最长串啦...当然要除去一些是由原本的节点剖分开的节点.这样的话一个节点仅仅只表示一个串.

    而最长公共后缀是十分好求的,我们熟知parent边就是一个不断取后缀的过程,然后两者不断删去首字母,直到后缀相同的时候也就到了它们在parent树上的LCA,LCA节点的最长长度就是它们的最长公共后缀.

    所以我们要算前缀的公共后缀就是考虑在一棵parent树上,考虑每个LCA可以是多少个点对的LCA.[这应该是一个比较基础的树型DP...]

    那么其实只要知道LCA的所有儿子的right集合大小即可.(因为right集合的每个位置分别表示了一段前缀[其实也就是这个节点下面的子树大小],)

    然后与它父亲的其它可能的前缀的可能相乘 就得到了有多少个点对.

     

    然后怎么求right集合的大小呢?

    有一个性质就是parent树是一个right集合合并的过程,那么一个点的right集合大小就是其所有子树right集合大小之和.

    还记得之前有画过一个图,现在再扯过来说一下:

    那么我们要是能把叶子节点的值赋值为1,然后沿着parent往上走就可以得到了.

    叶子节点怎么找呢?其实很简单...当你每次extend的时候,你会增加一个新的节点np,这个节点中就只包含一个元素{n},然后其它所有right集合中有n的都是它的祖先,所以它就是一个叶子节点,需要初始的时候就+1.

    而有时候我们将一个节点拆分成两个的时候,并没有产生新的元素,所以不是叶子节点不需要+1.

    当然有时候你不确定的原因是有的,比如:"abab"中的"ab"它明明有儿子,但是它确实又是一个叶子节点,提供了它自己right集合里面的2这个元素.如果把串最开始的部分加一个"无"的话,就可以看出来.

    ("ab")={2,4}--b-->("bab")={4}

         --无-->("无ab")={2}

    所以每个前缀都一定在叶子节点上...好神奇啊...然后大家记住就好了.

     

    当然还有一个树剖要满足的拓扑序的完成,这个就是按照长度来弄一下顺序就好了,越长的当然越在后面.具体的思路是一个联系桶排的思路,打过基数排序的同学应该会很熟悉.

    不懂的看代码就好.

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const int maxn=1000010;
    
    int n,last=1,cnt=1;
    int a[maxn][26],mx[maxn],fa[maxn];
    int T[maxn],Seq[maxn];
    int f[maxn],w[maxn];
    char ch[maxn];
    
    void extend(int c){
        int p=last,np=last=++cnt;
        mx[np]=mx[p]+1; f[np]=w[np]=1;
        while(!a[p][c] && p) a[p][c]=np,p=fa[p];
        if(!p) fa[np]=1;
        else{
            int q=a[p][c];
            if(mx[q]==mx[p]+1) fa[np]=q;
            else{
                int nq=++cnt;mx[nq]=mx[p]+1;
                memcpy(a[nq],a[q],sizeof(a[q]));
                fa[nq]=fa[q];
                fa[q]=fa[np]=nq;
                while(a[p][c]==q) a[p][c]=nq,p=fa[p];
            }
        }
    }
    
    void get_order(){
        for(int i=1;i<=cnt;i++) T[mx[i]]++;
        for(int i=1;i<=n;i++) T[i]+=T[i-1];
        for(int i=1;i<=cnt;i++) Seq[T[mx[i]]--]=i;
    }
    
    ll solve(){
        ll ans=0;
        for(int i=cnt;i>=1;i--) f[fa[Seq[i]]]+=f[Seq[i]];
        for(int i=cnt;i>=1;i--){
            int x=Seq[i];
            ans+=(ll)f[x]*w[fa[x]]*mx[fa[x]];
            w[fa[x]]+=f[x];
        }
        return ans;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("3238.in","r",stdin);
        freopen("3238.out","w",stdout);
    #endif
    
        scanf("%s",ch);
        n=strlen(ch);
        for(int i=n-1;i>=0;i--) extend(ch[i]-'a');
        
        ll ans=(ll)(1+n)*n*(n-1)/2;
        
        get_order();
        ans-=solve()*2;
        
        printf("%lld",ans);
        return 0;
    }
    View Code

     

  • 相关阅读:
    Dell PowerEdge服务器RAID卡驱动下载
    Cent OS yum 安装 Adobe flash player
    如何在安全模式下创建新管理员账户?
    chkdsk 和sfc.exe修复命令
    右下角弹出"Windows-延缓写入失败"或者"xxx-损坏文件 请运行Chkdsk工具"
    VMware NAT模式 Cent OS IP配置
    sublime Text2 2.0.2 build 2221 64位 破解(已测试)
    Windows Server 2008 R2 配置Exchange 2010邮件服务器
    openGL深度缓冲区问题
    glRotatef 转动方向
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/5591627.html
Copyright © 2011-2022 走看看