洛谷P4248 差异

题意:求所有后缀两两之间的最长公共前缀的长度之和。

解:这道题让我发现了一个奇妙的性质:所有后缀两两最长公共前缀长度之和 和 所有前缀两两最长公共后缀之和的值是相等的,但是每一组公共前/后缀是不同的。

因为这道题要反建后缀自动机,我正建然后过了......

两个串的最长公共后缀就是在fail树上的lca。

所以反建后缀自动机,所有后缀就是主链。然后求这些两两的lca计算每个点作为lca被统计了多少次,树上DFS一遍就好了。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 typedef long long LL;
 6 const int N = 1000010;
 7 
 8 struct Edge {
 9     int nex, v;
10 }edge[N]; int top;
11 
12 int tr[N][26], len[N], fail[N], cnt[N], tot = 1, last = 1;
13 int e[N], siz[N];
14 LL ans;
15 char s[N];
16 
17 inline void add(int x, int y) {
18     top++;
19     edge[top].v = y;
20     edge[top].nex = e[x];
21     e[x] = top;
22     return;
23 }
24 
25 inline void insert(char c) {
26     int f = c - 'a';
27     int p = last, np = ++tot;
28     last = np;
29     len[np] = len[p] + 1;
30     cnt[np] = 1;
31     while(p && !tr[p][f]) {
32         tr[p][f] = np;
33         p = fail[p];
34     }
35     if(!p) {
36         fail[np] = 1;
37     }
38     else {
39         int Q = tr[p][f];
40         if(len[Q] == len[p] + 1) {
41             fail[np] = Q;
42         }
43         else {
44             int nQ = ++tot;
45             len[nQ] = len[p] + 1;
46             fail[nQ] = fail[Q];
47             fail[Q] = fail[np] = nQ;
48             memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
49             while(tr[p][f] == Q) {
50                 tr[p][f] = nQ;
51                 p = fail[p];
52             }
53         }
54     }
55     return;
56 }
57 
58 inline void prework() {
59     for(int i = 2; i <= tot; i++) {
60         add(fail[i], i);
61         //printf("add : %d %d \n", fail[i], i);
62     }
63     return;
64 }
65 
66 void DFS(int x) {
67     siz[x] = cnt[x];
68     for(int i = e[x]; i; i = edge[i].nex) {
69         int y = edge[i].v;
70         DFS(y);
71         if(x > 1) {
72             ans += 1ll * siz[x] * siz[y] * len[x];
73         }
74         siz[x] += siz[y];
75     }
76     //printf("x = %d siz = %d \n", x, siz[x]);
77     return;
78 }
79 
80 int main() {
81     LL sum = 0;
82     scanf("%s", s);
83     int n = strlen(s);
84     for(int i = 0; i < n; i++) {
85         insert(s[i]);
86         sum += 1ll * (n - 1) * (i + 1);
87     }
88     prework();
89     DFS(1);
90     printf("%lld\n", sum - 2 * ans);
91     //printf("%lld - %lld \n", sum, 2 * ans);
92     return 0;
93 }
AC代码(伪)

 

posted @ 2019-01-29 20:12  huyufeifei  阅读(249)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜