bzoj 3238: [Ahoi2013]差異【SAM+樹形dp】
阿新 • • 發佈:2018-11-23
首先只有lcp(i,j)需要考慮
因為SAM的parent樹是字尾的字首的最長公共字尾(……),所以把這個串倒過來建SAM,這樣就變成了求兩個字首的最長公共字尾,長度就是這兩個字首在parent樹上的lcs對應的最大長度dis
這裡用treedp解決即可,就是合併一下size
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1000005; int n,fa[N],ch[N][27],dis[N],si[N],cur=1,con=1,la,h[N],cnt; long long ans; char s[N]; struct qwe { int ne,to; }e[N<<1]; void add(int u,int v) {//cerr<<u<<" "<<v<<endl; cnt++; e[cnt].ne=h[u]; e[cnt].to=v; h[u]=cnt; } void ins(int c,int id) { la=cur,dis[cur=++con]=id; int p=la; for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur; if(!p) fa[cur]=1; else { int q=ch[p][c]; if(dis[q]==dis[p]+1) fa[cur]=q; else { int nq=++con; dis[nq]=dis[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq]=fa[q]; fa[q]=fa[cur]=nq; for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } si[cur]=1; } void dfs(int u) { for(int i=h[u];i;i=e[i].ne) { dfs(e[i].to); ans-=2ll*dis[u]*si[u]*si[e[i].to]; si[u]+=si[e[i].to]; } } int main() { scanf("%s",s+1); n=strlen(s+1); reverse(s+1,s+1+n); for(int i=1;i<=n;i++) ins(s[i]-'a',i); for(int i=2;i<=con;i++) add(fa[i],i); dfs(1); for(int i=1;i<=n;i++) ans+=1ll*i*(n-1); printf("%lld\n",ans); return 0; }