洛谷 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 }

 

转载于:https://www.cnblogs.com/PinkRabbit/p/10124064.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值