[AHOI 2013] 差异

[题目链接]

        https://www.lydsy.com/JudgeOnline/problem.php?id=3238

[算法]

         首先 , LCP(Ti , Tj) = min{ height[rank[Ti] + 1] , height[rank[Ti] + 2] , ... , height[rank[Tj]] }

         显然 , 我们只要计算后缀两两之间的LCP之和即可

         对于一个右端点R , 随着L的减小 , min{height[L] , height[L + 1] .. , height[R]}的值单调递减 , 维护一个单调递增的单调栈即可

         时间复杂度 : O(NlogN)

[代码]

         

#include<bits/stdc++.h>
using namespace std;
#define MAXN 500010
typedef long long ll;
typedef long double ld;
const int inf = 1e9;

int n;
int height[MAXN] , cnt[MAXN] , rk[MAXN] , x[MAXN] , y[MAXN] , sa[MAXN];
char s[MAXN];

inline ll F(int x)
{
    return 1ll * x * (x + 1) / 2;
}
inline void build_sa()
{
    memset(cnt , 0 , sizeof(cnt));
    for (int i = 1; i <= n; i++) ++cnt[(int)s[i]];
    for (int i = 1; i <= 256; i++) cnt[i] += cnt[i - 1];
    for (int i = n; i >= 1; i--) sa[cnt[(int)s[i]]--] = i;
    rk[sa[1]] = 1;
    for (int i = 2; i <= n; i++) rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]);
    for (int k = 1; rk[sa[n]] != n; k <<= 1)
    {
        for (int i = 1; i <= n; i++)
            x[i] = rk[i] , y[i] = (i + k <= n) ? rk[i + k] : 0;
        memset(cnt , 0 , sizeof(cnt));
        for (int i = 1; i <= n; i++) ++cnt[y[i]];
        for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; i--) rk[cnt[y[i]]--] = i;
        memset(cnt , 0 , sizeof(cnt));
        for (int i = 1; i <= n; i++) ++cnt[x[i]];
        for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; i--) sa[cnt[x[rk[i]]]--] = rk[i];
        rk[sa[1]] = 1;
        for (int i = 1; i <= n; i++) rk[sa[i]] = rk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]); 
    }
}
inline void get_height()
{
    int k = 0;
    for (int i = 1; i <= n; i++)
    {
        if (k) --k;
        int j = sa[rk[i] - 1];
        while (s[i + k] == s[j + k]) ++k;
        height[rk[i]] = k;
    }    
}
inline ll calc_ans()
{
    int top = 0;
    ll ret = 0;
    static pair<ll , ll> s[MAXN];
    for (int i = 1; i <= n; i++) ret += 1ll * (i - 1) * (n - i + 1) + F(n) - F(n - i + 1);
    s[top = 1] = make_pair(0 , 0);
    ll now = 0;
    for (int i = 1; i <= n; i++)
    {
        int cnt = 1; 
        while (top > 0 && height[i] <= height[s[top].first]) 
        {
            cnt += s[top].second;
            now -= 1ll * height[s[top].first] * s[top].second;
            --top;
        }
        s[++top] = make_pair(i , cnt);
        now += 1ll * height[i] * cnt;
        ret -= 2ll * now;
    }
    return ret;
}

int main()
{
    
    scanf("%s" , s + 1);
    n = strlen(s + 1);
    build_sa();
    get_height();
    printf("%lld\n" , calc_ans());
    
    return 0;
}

 

posted @ 2018-11-30 22:18  evenbao  阅读(136)  评论(0编辑  收藏  举报