1. 程式人生 > >洛谷 P4248: bzoj 3238: [AHOI2013]差異

洛谷 P4248: bzoj 3238: [AHOI2013]差異

題目傳送門:洛谷 P4248

題意簡述:

定義兩個字串 \(S\) 和 \(T\) 的差異 \(\operatorname{diff}(S,T)\) 為這兩個串的長度之和減去兩倍的這兩個串的最長公共字首的長度。

給定一個字串,定義從第 \(i\) 個字元開始的字尾為 \(Suf_i\)。

求 \(\sum_{1\le i<j\le n}\operatorname{diff}(Suf_i,Suf_j)\)。

題解:

化簡式子,原式等於

\[\begin{align*}&\left(\sum_{1\le i<j\le n}i+j\right)-2\times\sum_{1\le i<j\le n}\operatorname{lcp}(Suf_i,Suf_j)\\=& \frac{n(n-1)(n+1)}{2}-2\times\sum_{1\le i<j\le n}\operatorname{lcp}(Suf_i,Suf_j)\end{align*}\]

所以只要求出後半部分即可。

建立字串的字尾陣列。

考慮 Height 陣列的貢獻:Height 陣列中 [2, n] 內的每一個區間都給答案貢獻區間最小值。

套路:每個區間的區間最小值之和,使用單調棧解決。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef long long LL;
 5 const int MN = 500005;
 6 
 7 int N;
 8 char str[MN];
 9 
10 int M;
11 int rk[MN], rk2[MN], SA[MN], SA2[MN];
12 int buk[MN], cnt; 13 int Height[MN]; 14 15 void GetHeight() { 16 int k = 0; 17 for (int i = 1; i <= N; ++i) { 18 if (rk[i] == 1) { k = Height[1] = 0; continue; } 19 if (k) --k; 20 int j = SA[rk[i] - 1]; 21 while (i + k <= N && j + k <= N && str[i + k] == str[j + k]) ++k;
22 Height[rk[i]] = k; 23 } 24 } 25 26 void Rsort() { 27 for (int i = 1; i <= M; ++i) buk[i] = 0; 28 for (int i = 1; i <= N; ++i) ++buk[rk[i]]; 29 for (int i = 1; i <= M; ++i) buk[i] += buk[i - 1]; 30 for (int i = N; i >= 1; --i) SA[buk[rk[SA2[i]]]--] = SA2[i]; 31 } 32 33 void GetSA() { 34 M = 26; 35 for (int i = 1; i <= N; ++i) rk[i] = str[i] - 'a' + 1, SA2[i] = i; 36 Rsort(); 37 for (int j = 1; j < N; j <<= 1) { 38 int P = 0; 39 for (int i = N - j + 1; i <= N; ++i) SA2[++P] = i; 40 for (int i = 1; i <= N; ++i) if (SA[i] > j) SA2[++P] = SA[i] - j; 41 Rsort(); 42 rk2[SA[1]] = P = 1; 43 for (int i = 2; i <= N; ++i) { 44 if (rk[SA[i]] != rk[SA[i - 1]] || rk[SA[i] + j] != rk[SA[i - 1] + j]) ++P; 45 rk2[SA[i]] = P; 46 } 47 for (int i = 1; i <= N; ++i) rk[i] = rk2[i]; 48 M = P; 49 if (M == N) break; 50 } 51 GetHeight(); 52 } 53 54 int st[MN], t; 55 int L[MN], R[MN]; 56 57 int main() { 58 scanf("%s", str + 1); 59 N = strlen(str + 1); 60 GetSA(); 61 st[t = 1] = 1; 62 for (int i = 2; i <= N; ++i) { 63 while (t && Height[st[t]] > Height[i]) R[st[t--]] = i; 64 L[i] = st[t]; 65 st[++t] = i; 66 } while (t) R[st[t--]] = N + 1; 67 LL Ans = (LL)(N - 1) * N * (N + 1) / 2; 68 for (int i = 2; i <= N; ++i) 69 Ans -= 2ll * (R[i] - i) * (i - L[i]) * Height[i]; 70 printf("%lld\n", Ans); 71 return 0; 72 }