zoukankan      html  css  js  c++  java
  • 【题解】[AHOI2013]差异

    [AHOI2013]差异

    ( ext{Solution:})

    观察一下原式:

    [sum_{i=1}^nsum_{j=i+1}^n ext{len}(i)+ ext{len}(j)-2 ext{lcp}(i,j) ]

    我们发现前面那个 ( ext{len}(i)+ ext{len}(j)) 好求。主要是后面的东西:两个后缀的 ( ext{LCP}) 怎么求。

    两个后缀的最长公共前缀……这个式子又长得很像树上的东西……后缀树似乎可以做这件事。

    考虑一下后缀树的性质:每一个叶子节点表示一个后缀(注意这里的叶子节点是指每一个后缀都显式地表现了出来),同时任意一个节点到根的路径都是对应一个后缀的前缀。

    那么两个后缀的 ( ext{LCP}) 不就是对应后缀树上的 (LCA!)

    考虑一个 (dp,) 直接把 (2 imes LCP(i,j)) 都算出来:设 (f[i]) 表示子树 (i) 的答案,若这个点不是后缀,那么就算出其 (siz) 后枚举孩子,计算 (sum_{vin son[x]} siz[v] imes(siz[x]-siz[v]) imes len[x]) 就是对的。因为每一对都算了两次。

    那么,如果当前点是一个,被某一个后缀所压缩掉的后缀信息,也即非显式表达出来的后缀呢?

    那么注意到,上述 (dp) 计算中实际上只计算了每一个叶子和它求 一次 (LCP) 的贡献。

    因为你无论枚举了哪一棵子树,这个当前的根,必然在这棵子树的外层,也就是对应每一棵子树的叶子,只会和它计算一次贡献。

    所以我们需要在最后加上这一层贡献。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N=1e6+10;
    typedef long long ll;
    namespace SAM{
        int len[N],pa[N],ch[N][26],tot=1,last=1,f[N];
        ll siz[N],ans;
        vector<int>G[N];
        void insert(const int &c){
            int p=last;
            int np=++tot;
            last=tot;siz[np]=1;
            len[np]=len[p]+1;
            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;
                    memcpy(ch[nq],ch[q],sizeof ch[q]);
                    pa[nq]=pa[q];pa[q]=pa[np]=nq;
                    len[nq]=len[p]+1;
                    for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
                }
            }
        }
        void dfs(int x){
            int psiz=siz[x];
            for(auto i:G[x]){
                dfs(i);
                siz[x]+=siz[i];
            }
            for(auto i:G[x]){
                ans+=siz[i]*(siz[x]-siz[i])*len[x];
                printf("%d ::%d %d
    ",x,siz[x],siz[i]);
            }
            printf("%lld:%lld %lld len: %lld lenfa:%lld pa:%lld
    ",x,psiz,siz[x],len[x],len[pa[x]],pa[x]);
            ans+=1ll*psiz*(siz[x]-psiz)*len[x];
        }
        void BuildTree(){
            for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
            dfs(1);
        }
    }
    char s[N],t[N];
    int slen;
    signed main(){
        scanf("%s",s+1);
        slen=strlen(s+1);
        for(int i=1;i<=slen;++i)t[slen-i+1]=s[i];
        for(int i=1;i<=slen;++i)putchar(t[i]);
        puts("");
        for(int i=1;i<=slen;++i)SAM::insert(t[i]-'a');
        SAM::BuildTree();
        ll sum=0;
        for(int i=1;i<=slen;++i)sum+=(slen-1)*(slen-i+1);
        printf("%lld
    ",sum-SAM::ans);
        return 0;
    }
    
  • 相关阅读:
    【转】有助于事业发展和幸福感提升的四个约定
    python命名空间的本质
    递归函数的定义和几个小例子
    全局变量与局部变量的使用问题
    计算机网络基础知识
    网络编程的基本概念,TCP/IP协议简介
    开博 第一篇
    Java8新特性之Stream
    深入理解HashMap(原理,查找,扩容)
    SQL优化|Java面试题
  • 原文地址:https://www.cnblogs.com/h-lka/p/15170558.html
Copyright © 2011-2022 走看看