BZOJ 3238: [Ahoi2013]差异 (后缀数组+单调栈)

题目要求在这里插入图片描述
所以Ans=n(n1)(n+1)221i<jnlcp(Ti,Tj)Ans=\frac{n*(n-1)*(n+1)}{2}-2*\sum_{1\le i<j\le n}lcp(T_i, T_j)

后面要减去的部分是两两后缀的lcplcp,那么做了后缀数组之后就是Ans=n(n1)(n+1)221<ijnMin(Heighti,Heightj)Ans=\frac{n*(n-1)*(n+1)}{2}-2*\sum_{1<i\le j\le n}Min(Height_i, Height_j)

后面其实就是[2,n][2,n]里面所有区间的最小值加起来再乘22.

这样我们考虑一个值可以作为最小值能够拓展的最左端和最右端,就可以算出这个值对答案的贡献.这个就单调栈就行了.具体见代码.

…mdzz又双叒叕缀写错后缀(chuo)数组了.

CODE

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 5e5+5;
char s[MAXN];
int n, x[MAXN], y[MAXN], c[MAXN], sa[MAXN], rk[MAXN], ht[MAXN];
inline void Get_Sa(int m) {
    for(int i = 1; i <= m; ++i) c[i] = 0;
    for(int i = 1; i <= n; ++i) ++c[x[i]=s[i]-'a'+1];
    for(int i = 2; i <= m; ++i) c[i] += c[i-1];
    for(int i = n; i >= 1; --i) sa[c[x[i]]--] = i;
    for(int k = 1; k <= n; k<<=1) {
        int 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 = 1; i <= m; ++i) c[i] = 0;
        for(int i = 1; i <= n; ++i) ++c[x[i]];
        for(int i = 2; i <= m; ++i) c[i] += c[i-1];
        for(int i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i];
        swap(x, y);
        x[sa[1]] = p = 1;
        for(int i = 2; i <= n; ++i)
            x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k]) ? p : ++p;
        if((m=p) == n) break;
    }
    for(int i = 1; i <= n; ++i) rk[sa[i]] = i;
}
inline void Get_Ht() {
    for(int i = 1, k = 0, j; i <= n; ++i) if(rk[i] > 1) {
        j = sa[rk[i]-1]; k = k ? k-1 : 0;
        while(i+k <= n && j+k <= n && s[i+k] == s[j+k]) ++k;
        ht[rk[i]] = k;
    }
}
int st[MAXN], top, L[MAXN], R[MAXN];
int main() {
	scanf("%s", s+1);
	n = strlen(s+1);
	Get_Sa(26); Get_Ht();
    L[st[top=1] = 1] = 0;
	for(int i = 2; i <= n; ++i) {
        while(top && ht[st[top]] > ht[i]) R[st[top--]] = i;
        L[i] = st[top]; st[++top] = i;
	}
	while(top) R[st[top--]] = n+1;
    LL ans = 1ll * (n-1) * n * (n+1) / 2;
	for(int i = 2; i <= n; ++i)
        ans -= 2ll * (i-L[i]) * (R[i]-i) * ht[i];
	printf("%lld\n", ans);
}


posted @ 2019-12-14 14:51  _Ark  阅读(85)  评论(0编辑  收藏  举报