BZOJ 3238 [Ahoi2013] 差异 | 后缀数组 单调栈
BZOJ 3238 [Ahoi2013] 差异 | 后缀数组 单调栈
题面
![](http://www.lydsy.com/JudgeOnline/upload/201306/1(4).jpg)
数据范围:\(2\le N\le 500000\)
题解
这是一道后缀数组模板题!
在学校停电没Wifi、自己流量又不舍得用的情况的逼迫下,我!终于!会自己背着写后缀数组了!
观察这个式子,发现\(len(T_i) + len(T_j)\)这部分和后面的完全没有关系嘛,所以拎出来单独处理,那么所求就变成了
\[(\sum_{1\le i < j \le n} len(T_i) + len(T_j)) - 2 * \sum_{1\le i < j \le n} lcp(T_i, T_j)
\]
左边那半部分就是\((n - 1) * n * (n + 1) / 2\)啦。至于右边那部分,就是任意一对后缀的lcp,显然使用后缀数组。
一开始我居然想写个维护后缀最大值的树状数组什么的来维护这个东西……
根据后缀数组的性质,把height数组看成一个序列,要求的就是每个子区间的最小值之和。这个东西当然是用单调栈来做啦!单调栈求出每个元素左边第一个比它大的位置和右边第一个比它大的位置,然后就可以求出有多少区间以它为最小值,区间数*这个最小值,加起来,就是答案啦。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define enter putchar('\n')
#define space putchar(' ')
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c > '9' || c < '0')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 500005;
int n, buf1[N], buf2[N], buc[N], sa[N], rnk[N], height[N], tol[N], tor[N], stk[N], top;
ll ans;
char s[N];
void suffix_sort(){
int *x = buf1, *y = buf2, m = 128;
for(int i = 0; i <= m; i++) buc[i] = 0;
for(int i = 1; i <= n; i++) buc[x[i] = s[i]]++;
for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
for(int i = n; i; i--) sa[buc[x[i]]--] = i;
for(int k = 1, p = 0; k <= n && p < n; k *= 2, m = p, p = 0){
for(int i = n - k + 1; i <= n; i++) y[++p] = i;
for(int i = 1; i <= n; i++) if(sa[i] > k) y[++p] = sa[i] - k;
for(int i = 0; i <= m; i++) buc[i] = 0;
for(int i = 1; i <= n; i++) buc[x[y[i]]]++;
for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
for(int i = n; i; i--) sa[buc[x[y[i]]]--] = y[i];
p = 1, swap(x, y), x[sa[1]] = 1;
for(int i = 2; i <= n; i++)
x[sa[i]] = (y[sa[i] + k] == y[sa[i - 1] + k] && y[sa[i]] == y[sa[i - 1]]) ? p : ++p;
}
for(int i = 1; i <= n; i++) rnk[sa[i]] = i;
for(int i = 1, k = 0; i <= n; i++){
if(rnk[i] == 1) continue;
int j = sa[rnk[i] - 1];
if(k) k--;
while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
height[rnk[i]] = k;
}
}
int main(){
scanf("%s", s + 1), n = strlen(s + 1);
suffix_sort();
stk[top = 1] = 1;
for(int i = 2; i <= n; i++){
while(top > 1 && height[i] < height[stk[top]]) tor[stk[top--]] = i;
tol[i] = stk[top];
stk[++top] = i;
}
while(top > 1) tor[stk[top--]] = n + 1;
for(int i = 2; i <= n; i++)
ans += (ll) height[i] * (i - tol[i]) * (tor[i] - i);
write((ll)(n + 1) * n / 2 * (n - 1) - 2 * ans), enter;
return 0;
}
本文作者:胡小兔
博客地址:http://rabbithu.cnblogs.com
博客地址:http://rabbithu.cnblogs.com