[BZOJ 3238] [AHOI 2013] 差异

题目链接:BZOJ - 3238

题目分析

显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显。

求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组。

那么我们可以用这个 Height 数组求出所有后缀之间 LCP 的和。

我们用 f[i] 表示字典序第 i 的后缀与字典序在 i 之后的所有后缀的 LCP 的和。

我们知道,两个后缀的 LCP 为 Height 数组中这两个后缀之间的最小值。

我们从最后向前推 i ,用一个单调栈维护后面的 Height 单调不上升,然后用 St[Top] 来推 f[i] 即可,具体见代码。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int MaxL = 500000 + 5;

typedef long long LL;

LL Ans, Temp;
LL f[MaxL];

int n, Top;
int A[MaxL], Rank[MaxL], SA[MaxL], Height[MaxL], St[MaxL];
int VA[MaxL], VB[MaxL], VC[MaxL], Sum[MaxL];

char S[MaxL];

inline bool Cmp(int *a, int x, int y, int l) {
    return (a[x] == a[y]) && (a[x + l] == a[y + l]);
}

void DA(int *A, int n, int m) {
    int *x, *y, *t;
    x = VA; y = VB;
    for (int i = 1; i <= m; ++i) Sum[i] = 0;
    for (int i = 1; i <= n; ++i) ++Sum[x[i] = A[i]];
    for (int i = 2; i <= m; ++i) Sum[i] += Sum[i - 1];
    for (int i = n; i >= 1; --i) SA[Sum[x[i]]--] = i;
    int p, q;
    p = 0;
    for (int j = 1; p < n; j <<= 1, m = p) {
        q = 0;
        for (int i = n - j + 1; i <= n; ++i) y[++q] = i;
        for (int i = 1; i <= n; ++i) {
            if (SA[i] <= j) continue;
            y[++q] = SA[i] - j;
        }
        for (int i = 1; i <= m; ++i) Sum[i] = 0;
        for (int i = 1; i <= n; ++i) VC[i] = x[y[i]];
        for (int i = 1; i <= n; ++i) ++Sum[VC[i]];
        for (int i = 2; i <= m; ++i) Sum[i] += Sum[i - 1];
        for (int i = n; i >= 1; --i) SA[Sum[VC[i]]--] = y[i];
        t = x; x = y; y = t; p = 1;
        x[SA[1]] = 1;
        for (int i = 2; i <= n; ++i)
            x[SA[i]] = Cmp(y, SA[i], SA[i - 1], j) ? p : ++p;
    }
    for (int i = 1; i <= n; ++i) Rank[SA[i]] = i;

    //GetHeight
    int h = 0, o;
    for (int i = 1; i <= n; ++i) {
        if (Rank[i] == 1) continue;
        o = SA[Rank[i] - 1];
        while (A[i + h] == A[o + h]) ++h;
        Height[Rank[i]] = h;
        if (h > 0) --h;
    }
}

int main()
{
    scanf("%s", S + 1);
    n = strlen(S + 1);
    for (int i = 1; i <= n; ++i) A[i] = S[i] - ‘a‘ + 1;
    DA(A, n, 26);
    Ans = 0ll; Temp = 0ll;
    for (int i = 1; i <= n; ++i)
        Ans += (LL)(n - i + 1) * (LL)(n - 1);
    Top = 0;
    St[++Top] = n + 1;
    for (int i = n; i >= 2; --i) {
        while (Top > 0 && Height[St[Top]] > Height[i]) --Top;
        int x = St[Top];
        f[i] = (LL)Height[i] + (LL)Height[i] * (x - i - 1) + (LL)f[x];
        Temp += f[i];
        St[++Top] = i;
    }
    Ans -= Temp * 2ll;
    printf("%lld\n", Ans);
    return 0;
}

  

时间: 2024-12-15 07:11:46

[BZOJ 3238] [AHOI 2013] 差异的相关文章

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

BZOJ 3238 AHOI 2013 差异 后缀树

题目大意:求所有后缀长度减去LCP长度的二倍. 思路:之前用后缀数组写过,但是做法并不是很直观.现在学了后缀树再来写一次,这次思路就很清晰了. 首先我们把字符串按照倒序插入到后缀树中.形成的后缀树有一个很好的性质,连个后缀节点的LCA就是这两个后缀的LCP的位置,LCA的len值自然就是两个后缀的LCP. 建好树之后,进行一次树形DP,统计出来每两个后缀的LCP长度,计入总答案. 这东西以后不用指针写了,简直DT死了.. CODE: #include <cstdio> #include <

【BZOJ 3238】 [Ahoi2013]差异

3238: [Ahoi2013]差异 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 777 Solved: 359 [Submit][Status][Discuss] Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N<=500000,S由小写英文字母组成 后缀数组+单调队列. 式子中前两项的和可以直接求出为

[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] ..

BZOJ 3236 AHOI 2013 作业 莫队算法

题目大意:给出一些数,问在一个区间中不同的数值有多少种,和在一个区间中不同的数值有多少个. 思路:由于没有修改,所以就想到了莫队算法.然后我写了5K+的曼哈顿距离最小生成树,然后果断T了.(100s的时限啊,刷status都要刷疯了..,结果最后加了手写读入也没能A).后来果断放弃,写了分块版的莫队算法.84sAC...这题卡的..貌似莫队并不是正解. 其实用分块来写莫队就很简单了.只需要将所有询问的区间排序,左端点所在块作为第一键值,右端点作为第二季键值排序,之后就可以转移了.理论上来时还是曼

【BZOJ 3238】 3238: [Ahoi2013]差异(SAM)

3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3047  Solved: 1375 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N<=500000,S由小写英文字母组成 Source [分析] 这题先把sigma len 加上. 然后考虑一下减掉的是什么. 对于每个子

bzoj 3238差异

3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1420  Solved: 662[Submit][Status][Discuss] Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N<=500000,S由小写英文字母组成 题解: right集合:pnt树上的子树sz大小(

bzoj 3238: [Ahoi2013]差异 -- 后缀数组

3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MB Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N<=500000,S由小写英文字母组成 Source 后缀数组+单调栈水过... #include<map> #include<cmath> #include<

BZOJ 3238 [Ahoi2013]差异(后缀自动机)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3238 [题目大意] 给出一个串,设T[i]表示从第i位开始的后缀, 求sum(len(T[i])+len(T[j])-2*lcp(T[i],T[j])) [题解] 根据反串的后缀自动机建立后缀树, 则两点的LCA在自动机中的length就是他们的LCP, 树形DP统计一下即可. [代码] #include <cstdio> #include <algorithm> #i