【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由小写英文字母组成

后缀数组+单调队列。

式子中前两项的和可以直接求出为(n?1)?[n?(n+1)/2],而后面一项肯定是基于后缀数组的height[i]数组来计算。

假设height[i]在L[i]到R[i]之间是最小的,那么从L[i]到i中任取一个,从i到R[i]中任取一个,这两个串的最长公共前缀就是height[i]了。

那么我们只要用单调队列来求出i左边第一个比height[i]大的位置,右边第一个比height[i]大的位置,并分别+1,?1即可。

但是这样做并不对,因为没有处理height值相同的情况,其实只要在求某一边的时候加个等号即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#define M 500005
#define LL long long
using namespace std;
int c[M],wv[M],x[M],y[M],wa[M],wb[M],a[M],L[M],R[M],sa[M],rk[M],he[M],q[M];
char s[M];
int n;
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int *x=wa,*y=wb,*t,i,j,p;
    for (i=0;i<m;i++)
        c[i]=0;
    for (i=0;i<n;i++)
        c[x[i]=r[i]]++;
    for (i=1;i<m;i++)
        c[i]+=c[i-1];
    for (i=n-1;i>=0;i--)
        sa[--c[x[i]]]=i;
    for (j=1,p=1;p<n;j*=2,m=p)
    {
        for (p=0,i=n-j;i<n;i++)
            y[p++]=i;
        for (i=0;i<n;i++)
            if (sa[i]>=j)
                y[p++]=sa[i]-j;
        for (i=0;i<n;i++)
            wv[i]=x[y[i]];
        for (i=0;i<m;i++)
            c[i]=0;
        for (i=0;i<n;i++)
            c[wv[i]]++;
        for (i=1;i<m;i++)
            c[i]+=c[i-1];
        for (i=n-1;i>=0;i--)
            sa[--c[wv[i]]]=y[i];
        for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
}
void Calheight(int *r,int *sa,int n)
{
    for (int i=1;i<=n;i++)
        rk[sa[i]]=i;
    int k=0;
    for (int i=0;i<n;i++)
    {
        if (k) k--;
        int j=sa[rk[i]-1];
        while (r[i+k]==r[j+k])
            k++;
        he[rk[i]]=k;
    }
}
void Getlr()
{
    int r=1;
    L[2]=2;
    q[1]=2;
    q[0]=1;
    for (int i=3;i<=n;i++)
    {
        while (r>=1&&he[q[r]]>he[i])
            r--;
        q[++r]=i;
        L[i]=q[r-1]+1;
    }
    R[n]=n;
    r=1;
    q[1]=n;
    q[0]=n+1;
    for (int i=n-1;i>1;i--)
    {
        while (r>=1&&he[q[r]]>=he[i])
            r--;
        q[++r]=i;
        R[i]=q[r-1]-1;
    }
}
int main()
{
    scanf("%s",s);
    n=strlen(s);
    for (int i=0;i<n;i++)
        a[i]=s[i]-‘a‘+1;
    a[n]=0;
    da(a,sa,n+1,27);
    Calheight(a,sa,n);
    he[0]=he[n+1]=-1;
    Getlr();
    LL ans=1LL*n*(n+1)*(n-1)/2LL;
    for (int i=2;i<=n;i++)
        ans-=2LL*he[i]*(i-L[i]+1)*(R[i]-i+1);
    printf("%lld\n",ans);
    return 0;
}

时间: 2024-10-12 08:45:53

【BZOJ 3238】 [Ahoi2013]差异的相关文章

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

●BZOJ 3238 [Ahoi2013]差异

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3238 题解: 后缀数组套路深. 问题转化为求出任意两个后缀的LCP之和 在计算贡献时,各种不爽,然后就套路的从height[i]数组下手.计算出 L[i]和 R[i],L[i]:找出排名最小(即为 L[i])的后缀与排名为 i的后缀的 LCP==hei[i]R[i]:找出排名最大(即为 R[i])的后缀与排名为 i的后缀的 LCP==hei[i](更直白一点就是在hei数组中找出最大的包含

BZOJ 3238: [Ahoi2013]差异 后缀自动机 树形dp

http://www.lydsy.com/JudgeOnline/problem.php?id=3238 就算是全局变量,也不要忘记,初始化(吐血). 长得一副lca样,没想到是个树形dp(小丫头还有两幅面孔呢). 看代码实现吧,不大容易口头解释,把加的和减的分开算就可以了,减去的通过倒着建sam(相当于建一棵后缀树),然后算每个len取的次数实现,注意树归中一些避免重复操作. 1 /********************************************************

bzoj 3238: [Ahoi2013]差异

一看字符串 最长公共前缀,用后缀数组+单调栈搞搞就行啦.一定要注意long long 啊 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<queue> 7 #include<algorithm> 8 #include<vector> 9 #de

BZOJ 3238 AHOI2013 差异 后缀自动机

题目大意:给定一个字符串,求Σ[1<=i<j<=n]|Ti|+|Tj|-2|LCP(Ti,Tj)| 前两项是可以O(1)求的 我们要求的就是LCP之和 对反串建立后缀自动机 那么parent指针连成的树就是后缀树 直接在后缀树上DP就行- - 对于每个节点统计所有子树两两right集合大小乘积之和乘上这个节点的深度即可 QY神在学校讲了一天的SAM... 现在我觉得我还是回去学大型建筑机械吧233- - #include <map> #include <vector&g

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: [Ahoi2013]差异【SAM+树形dp】

首先只有lcp(i,j)需要考虑 因为SAM的parent树是后缀的前缀的最长公共后缀(--),所以把这个串倒过来建SAM,这样就变成了求两个前缀的最长公共后缀,长度就是这两个前缀在parent树上的lcs对应的最大长度dis 这里用treedp解决即可,就是合并一下size #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace st

【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】差异 后缀自动机+树形DP

题意 给定字符串,令$s_i$表示第$i$位开始的后缀,求$\sum_{1\le i < j \le n} len(s_i)+len(s_j)-2\times lcp(s_i,s_j)$ 先考虑前面的和式,直接计算为$\frac{n(n^2-1)}{2}$,考虑后面的和式,$lcp$相关可以用sam求解,sam形成的parent树是原串的前缀树,所以两个串的最长公共后缀是在parent树上最近公共祖先对应的状态的长度$maxlen_s-maxlen_{pa_s}$,将原串反向建立sam得到后缀树