码迷,mamicode.com
首页 > 其他好文 > 详细

【AHOI 2013】差异

时间:2020-03-02 10:44:00      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:void   --   排名   tchar   单调栈   name   math   计算   ||   

\((\displaystyle \sum^{}_{1<=i<j<=n}{i+j})-2*\displaystyle \sum^{}_{1<=i<j<=n}{lcp(suf(i),suf(j))}\)

\(=(n-1)*Σi-2*\displaystyle \sum^{}_{1<=i<j<=n}{lcp(suf(i),suf(j))}\)

\(=\frac{(n-1)*(1+n)*n}{2}-2*\displaystyle \sum^{}_{1<=i<j<=n}{lcp(suf(i),suf(j))}\)

注意前者的\(i\)\(j\)是转换了一下的,为了后面化简顺利。(也就是说前面的\(i\)\(j\)与后面的\(i\)\(j\)不是对应一样的)

我们就可以考虑后面的环节。因为暴枚的时间是\(n\)方,我们肯定过不去。所以只有用一个小一点的枚举。

可以利用\(h\)数组。因为两个后缀的\(lcp\)是排名在它们之间的串的\(h\)值的\(min\)。反过来思考,我们只要计算出以\(i\)为中心的\(h\)值比\(h[i]\)大的连续区间的长度就行了。我们可以用单调栈维护。

统计出两边的个数后我们相乘就能得到\(lcp\)\(h[i]\)的区间个数了。

注意处理最后仍未被弹出栈的元素。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 5e5 + 3;

long long ans;
int cnt, l[N], r[N], s[N], lg[N], T, rmq[N][20], n, m, h[N], tax[N], SA[N], tp[N], rk[N], a[N];

int read() {
    int x = 0, f = 1; char S;
    while((S = getchar()) > '9' || S < '0') {
        if(S == '-') f = -1;
        if(S == EOF) exit(0);
    }
    while(S <= '9' && S >= '0') {
        x = (x << 1) + (x << 3) + (S ^ 48);
        S = getchar();
    }
    return x * f;
}

bool cmp(const int x, const int y, const int d) {return tp[x] == tp[y] && tp[x + d] == tp[y + d];}

void Sort() {
    for(int i = 0; i <= m; ++ i) tax[i] = 0;
    for(int i = 1; i <= n; ++ i) ++ tax[rk[tp[i]]];
    for(int i = 1; i <= m; ++ i) tax[i] += tax[i - 1];
    for(int i = n; i >= 1; -- i) SA[tax[rk[tp[i]]] --] = tp[i];
}

void Swap(int &x, int &y) {
    x ^= y ^= x ^= y;
}

int Min(const int x, const int y) {
    if(x < y) return x;
    return y;
}

void Suffix() {
    for(int i = 1; i <= n; ++ i) rk[i] = a[i], tp[i] = i;
    m = 122; Sort();
    for(int w = 1, p = 1, i; p < n; m = p, w <<= 1) {
        for(p = 0, i = n - w + 1; i <= n; ++ i) tp[++ p] = i;
        for(i = 1; i <= n; ++ i) if(SA[i] > w) tp[++ p] = SA[i] - w;
        Sort(); swap(rk, tp); rk[SA[1]] = p = 1;
        for(i = 2; i <= n; ++ i) rk[SA[i]] = cmp(SA[i], SA[i - 1], w) ? p : ++ p;
    }
    int j, k = 0;
    for(int i = 1; i <= n; h[rk[i ++]] = k)
        for(k = k ? k - 1 : k, j = SA[rk[i] - 1]; a[i + k] == a[j + k]; ++ k);
    for(int i = 1; i <= n; ++ i) rmq[i][0] = h[i];
    for(int j = 1; (1 << j) <= n; ++ j)
        for(int i = 1; i + (1 << j) - 1 <= n; ++ i)
            rmq[i][j] = Min(rmq[i][j - 1], rmq[i + (1 << j - 1)][j - 1]);
}

int lcp(const int x, const int y) {
    int l = rk[x], r = rk[y];
    if(l > r) Swap(l, r);
    int t = lg[r - l];
    return Min(rmq[l + 1][t], rmq[r - (1 << t) + 1][t]);
}

int main() {
    char ch[N];
    scanf("%s", ch); n = strlen(ch);
    for(int i = 1; i <= n; ++ i) a[i] = ch[i - 1];
    Suffix();
    ans = 1ll * n * (n - 1) * (n + 1) / 2;
    s[cnt = 1] = 1;
    for(int i = 2; i <= n; ++ i) {
        while(cnt && h[s[cnt]] > h[i]) r[s[cnt --]] = i;
        l[i] = s[cnt];
        s[++ cnt] = i;
    }
    while(cnt) r[s[cnt --]] = n + 1;
    for(int i = 2; i <= n; ++ i)
        ans -= 2ll * (i - l[i]) * (r[i] - i) * h[i];
    printf("%lld\n", ans);
    return 0;
}

【AHOI 2013】差异

标签:void   --   排名   tchar   单调栈   name   math   计算   ||   

原文地址:https://www.cnblogs.com/AWhiteWall/p/12394034.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!