[AHOI2013]差异 【SAM后缀树+树形dp】
[AHOI2013]差异
万能的后缀树啊2333
建出后缀树,一个节点的祖先节点都是他的后缀,一个节点的儿子节点都是以他为后缀的串。所以两个串的lcs就是他们的lca节点的 l e n len len 。
这道题可以对反串建后缀树,树边 ( u , v ) (u,v) (u,v) 权值为 ∣ l e n u − l e m v ∣ |len_u-lem_v| ∣lenu−lemv∣ ,答案就是所有关键点之间的路径权值之和。
#include<bits/stdc++.h>
#define N 1000006
using namespace std;
typedef long long ll;
char s[N];
int lst,cnt,link[N];
ll n,ans,len[N],siz[N];
vector<int> son[N];
struct SAM{
map<char,int> trans[N];
void insert(char c){
int z=++cnt,p=lst; siz[z]=1,len[z]=len[lst]+1;
while(p!=-1&&!trans[p].count(c))trans[p][c]=z,p=link[p];
if(p==-1) link[z]=0;
else{
int q=trans[p][c];
if(len[q]==len[p]+1) link[z]=q;
else{
int clone=++cnt; len[clone]=len[p]+1;
trans[clone]=trans[q];
while(p!=-1&&trans[p][c]==q) trans[p][c]=clone,p=link[p];
link[clone]=link[q],link[z]=link[q]=clone;
}
}
lst=z;
}
void dp(int x=0){
int y;
for(int i=0;i<son[x].size();i++) y=son[x][i],dp(y),siz[x]+=siz[y];
if(x) ans+=siz[x]*(n-siz[x])*(len[x]-len[link[x]]);
}
}sam;
int main(){
scanf("%s",s+1); n=strlen(s+1);
link[0]=-1;
for(int i=n;i>=1;i--) sam.insert(s[i]);
for(int i=1;i<=cnt;i++) son[link[i]].push_back(i);
sam.dp();
cout<<ans;
}