1. 程式人生 > 其它 >【題解】[AHOI2013]差異

【題解】[AHOI2013]差異

[AHOI2013]差異

\(\text{Solution:}\)

觀察一下原式:

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

我們發現前面那個 \(\text{len}(i)+\text{len}(j)\) 好求。主要是後面的東西:兩個字尾的 \(\text{LCP}\) 怎麼求。

兩個字尾的最長公共字首……這個式子又長得很像樹上的東西……字尾樹似乎可以做這件事。

考慮一下字尾樹的性質:每一個葉子節點表示一個字尾(注意這裡的葉子節點是指每一個字尾都顯式地表現了出來),同時任意一個節點到根的路徑都是對應一個字尾的字首。

那麼兩個字尾的 \(\text{LCP}\) 不就是對應字尾樹上的 \(LCA!\)

考慮一個 \(dp,\) 直接把 \(2\times LCP(i,j)\) 都算出來:設 \(f[i]\) 表示子樹 \(i\) 的答案,若這個點不是字尾,那麼就算出其 \(siz\) 後列舉孩子,計算 \(\sum_{v\in son[x]} siz[v]\times(siz[x]-siz[v])\times 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\n",x,siz[x],siz[i]);
        }
        printf("%lld:%lld %lld len: %lld lenfa:%lld pa:%lld\n",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\n",sum-SAM::ans);
    return 0;
}