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:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 500010
using namespace std;
#define min(a,b) ((a) < (b) ? (a):(b))

char s[MAX];
int len;

int val[MAX],sa[MAX];
int height[MAX],rank[MAX];

inline bool Same(int x,int y,int l)
{
    return val[x] == val[y] &&
    ((x + l >= len && y + l >= len) || (x + l < len && y + l < len && val[x + l] == val[y + l]));
}

void GetSuffixArray()
{
    static int _val[MAX],q[MAX],cnt[MAX],lim = 256;
    for(int i = 0; i < len; ++i) ++cnt[val[i] = s[i]];
    for(int i = 1; i < lim; ++i) cnt[i] += cnt[i - 1];
    for(int i = len - 1; ~i; --i)   sa[--cnt[val[i]]] = i;
    for(int d = 1;; ++d) {
        int top = 0,l = 1 << (d - 1);
        for(int i = 0; i < len; ++i) if(sa[i] + l >= len) q[top++] = sa[i];
        for(int i = 0; i < len; ++i) if(sa[i] >= l)   q[top++] = sa[i] - l;

        for(int i = 0; i < lim; ++i) cnt[i] = 0;
        for(int i = 0; i < len; ++i) ++cnt[val[q[i]]];
        for(int i = 1; i < lim; ++i) cnt[i] += cnt[i - 1];
        for(int i = len - 1; ~i; --i)   sa[--cnt[val[q[i]]]] = q[i];
        lim = 0;
        for(int i = 0,j; i < len; ++lim) {
            for(j = i; j < len - 1 && Same(sa[j],sa[j + 1],l); ++j);
            for(; i <= j; ++i)   _val[sa[i]] = lim;
        }
        for(int i = 0; i < len; ++i) val[i] = _val[i];
        if(lim == len)  break;
    }
    return ;
}

void GetHeight()
{
    for(int i = 0; i < len; ++i) rank[sa[i]] = i;
    for(int i = 0,k = 0; i < len; ++i) {
        if(!rank[i])    continue;
        if(k)   --k;
        int j = sa[rank[i] - 1];
        while(s[i + k] == s[j + k]) ++k;
        height[rank[i]] = k;
    }
}

int l[MAX],r[MAX];
int stack[MAX],top;

int main()
{
    scanf("%s",s);
    len = strlen(s);
    GetSuffixArray();
    GetHeight();
    long long ans = (long long)(len + 1) * len * (len - 1) >> 1;
    for(int i = 1; i < len; ++i) {
        while(top && height[stack[top]] > height[i]) --top;
        if(!top)    l[i] = 0;
        else    l[i] = stack[top];
        stack[++top] = i;
    }
    top = 0;
    for(int i = len - 1; i; --i) {
        while(top && height[stack[top]] >= height[i])    --top;
        if(!top)    r[i] = len;
        else    r[i] = stack[top];
        stack[++top] = i;
    }
    for(int i = 1; i < len; ++i)
        ans -= (long long)(i - l[i]) * (r[i] - i) * height[i] * 2;
    printf("%lld\n",ans);
    return 0;
}

时间: 2024-10-20 19:30:15

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈的相关文章

BZOJ 3238 AHOI 2013 差异 后缀树

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

【bzoj3238】[Ahoi2013]差异 后缀数组+单调栈

题目描述 输入 一行,一个字符串S 输出 一行,一个整数,表示所求值 样例输入 cacao 样例输出 54 题解 后缀数组+单调栈,几乎同 bzoj3879 的后半部分. 我明显是做题做反了... 这里还是说一下这道题的做法. 先用后缀数组求出height. 然后由于有LCP(a,c)=min(LCP(a,b),LCP(b,c))(rank[a]<rank[b]<rank[c]),所以我们只需要知道排名相邻的两个后缀的LCP,而这就是height数组的定义. 转化为子问题:给出n个数,求所有子

bzoj 3238 [Ahoi2013]差异 后缀数组 + 单调栈

题目链接 Description 一个长度为\(n\)的字符串\(S\),令\(T_i\)表示它从第\(i\)个字符开始的后缀.求\[\sum_{1\leq i\leq j\leq n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\]其中,\(len(a)\)表示字符串\(a\)的长度,\(lcp(a,b)\)表示字符串\(a\)和字符串\(b\)的最长公共前缀. \(2\leq n\leq 500000\) 思路 \(O(n^2)\)枚举显然是不可行的,应从 贡献 的角度取

[BZOJ 3238] [AHOI 2013] 差异

题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么我们可以用这个 Height 数组求出所有后缀之间 LCP 的和. 我们用 f[i] 表示字典序第 i 的后缀与字典序在 i 之后的所有后缀的 LCP 的和. 我们知道,两个后缀的 LCP 为 Height 数组中这两个后缀之间的最小值. 我们从最后向前推 i ,用一个单调栈维护后面的 Height

bzoj3238 [Ahoi2013]差异 后缀数组+单调栈

[bzoj3238][Ahoi2013]差异 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 题解: 任意两个字符串的lcp是什么,就是如 a,b  那么若a==b 那么为len(a) 否则设sa[a]<sa[b] 那么为min(height[sa[a]+1-------sa[b]]) 1 #include<cstring> 2 #include<iostrea

HUID 5558 Alice&#39;s Classified Message 后缀数组+单调栈+二分

http://acm.hdu.edu.cn/showproblem.php?pid=5558 对于每个后缀suffix(i),想要在前面i - 1个suffix中找到一个pos,使得LCP最大.这样做O(n^2) 考虑到对于每一个suffix(i),最长的LCP肯定在和他排名相近的地方取得. 按排名大小顺序枚举位置,按位置维护一个递增的单调栈,对于每一个进栈的元素,要算一算栈内元素和他的LCP最大是多少. 如果不需要输出最小的下标,最大的直接是LCP(suffix(st[top]),  suff

[HAOI2016]找相同字符(后缀数组+单调栈)

[HAOI2016]找相同字符(后缀数组+单调栈) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 我们把两个字符串接在一起,中间加一个分隔符.如\(\text{AABB}\)和\(\text{BBAA}\)变成\(\text{AABB|BBAA}\).我们考虑两个相同字串,如\(\text{BB}\),它在新串中对应了两个后缀\(BB|BBAA\)和\(\text{BBAA}\)的LCP. 容易发现,LC

[bzoj3238]差异(后缀数组+单调栈)

显然我们可以先把len(Ti)+len(Tj)的值先算出来,再把LCP减去.所有len(Ti)+len(Tj)的值为n*(n-1)*(n+1)/2,这个随便在纸上画一画就可以算出来的. 接下来问题就是如何把LCP减去.我们先用后缀数组把height求出来,当有一段区间l~r,height[i]为height[l]~height[r]中的最小值,那么在l~r当中随便取两个后缀,他们的LCP则都是height[i],这个很好理解吧.那么l~r这个区间里有(l-i+1)*(r-i+1)对后缀,所以我们

bzoj 4199: [Noi2015]品酒大会【后缀数组+单调栈+并查集】

用SA求出height数组,然后发现每个height值都有一个贡献区间(因为点对之间要依次取min) 用单调栈处理出区间,第一问就做完了 然后用并查集维护每个点的贡献(?),从大到小枚举height,因为这样区间是不断增大的所以并查集合并即可 #include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; const int N=300