[bzoj3238]差异

对于len(Ti),len(Tj),随意乱搞就行了。

对于lcp(Ti,Tj),很显然如果将所有的后缀都搞出来的话比较好弄。

既然是要求lcp的和,那么搞到后缀树上去找节点就行了。

因为后缀树有压缩,所以没有分叉,直接树上dp就行。(即使有分叉也没啥卵用。)

但是如何统计分叉呢?

计数原理。

对于每一个分叉,很显然构成的后缀的数量是在所有结束集合中任选两个,那么就是C2sz[x],那么就好搞了,最后乘上一个长度。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <string>
 7 #include <vector>
 8 #include <cmath>
 9 #include <queue>
10 #include <complex>
11 #include <set>
12 using namespace std;
13 
14 const int N=1500000;
15 
16 int ch[N][30],len[N],pre[N],r[N],tot=1,last=1;
17 int to[N],head[N],rest[N],num;
18 int p,np,q,nq;
19 long long ans;
20 char str[N];
21 
22 void add(int u,int v){
23     to[++num]=v;
24     rest[num]=head[u];
25     head[u]=num;
26 }
27 
28 int nd(int l){return len[++tot]=l,tot;}
29 
30 void add(int v){
31     last=np=nd(len[p=last]+1);
32     pre[np]=r[np]=1;
33     while(p&&!ch[p][v])ch[p][v]=np,p=pre[p];
34     if(p){
35         pre[np]=q=ch[p][v];
36         if(len[p]+1!=len[q]){
37             nq=nd(len[p]+1);
38             memcpy(ch[nq],ch[q],sizeof ch[q]);
39             pre[nq]=pre[q],pre[q]=pre[np]=nq;
40             while(p&&ch[p][v]==q)ch[p][v]=nq,p=pre[p];
41         }
42     }
43 }
44 
45 void dp(int x){
46     for(int i=head[x];i;i=rest[i]){
47         dp(to[i]);
48         r[x]+=r[to[i]];
49     }
50     ans-=(long long)r[x]*(r[x]-1)*(len[x]-len[pre[x]]);
51 }
52 
53 int main(){
54     scanf("%s",str);
55     int len=strlen(str);
56     ans=(long long)len*(len-1)*(len+1)/2;
57     for(int i=len-1;i>=0;i--)add(str[i]-'a');
58     for(int i=2;i<=tot;i++)add(pre[i],i);
59     for(int i=head[1];i;i=rest[i])dp(to[i]);
60     printf("%lld\n",ans);
61 }
View Code

 

posted @ 2017-02-24 20:19  KingSann  阅读(153)  评论(0编辑  收藏  举报