BZOJ3238: [Ahoi2013]差异 (后缀自动机)

Description

Input

一行,一个字符串S

Output

一行,一个整数,表示所求值

Sample Input

cacao

Sample Output

54

HINT

2<=N<=500000,S由小写英文字母组成

YY了后缀自动机的解法:

首先题意就是让你求sigma(LCP(i,j)|i<j)

将字符串反过来,考虑两个后缀对答案的贡献,其实就是节点x和y的lca节点包含的最长子串长度

那么将SAM构出来,考虑当LCA为节点z时,有多少满足条件的(x,y),这个枚举z的相邻子节点,dp一下即可

code:O(n) 2104ms

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1000010;
int n,to[maxn][26],fa[maxn],l[maxn],f[maxn],x[maxn],w[maxn],od[maxn],cnt=1,last=1;
void extend(int c)
{
    int p,q,np,nq;
    p=last;last=np=++cnt;l[np]=l[p]+1;f[np]=w[np]=1;
    for(;!to[p][c];p=fa[p]) to[p][c]=np;
    if(!p) fa[np]=1;
    else
    {
        q=to[p][c];
        if(l[p]+1==l[q]) fa[np]=q;
        else
        {
            nq=++cnt;l[nq]=l[p]+1;
            memcpy(to[nq],to[q],sizeof(to[q]));
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            for(;to[p][c]==q;p=fa[p]) to[p][c]=nq;
        }
    }
}
LL solve()
{
    LL ans=0;
    for(int i=1;i<=cnt;i++) x[l[i]]++;
    for(int i=1;i<=n;i++) x[i]+=x[i-1];
    for(int i=1;i<=cnt;i++) od[x[l[i]]--]=i;
    for(int i=cnt;i;i--) f[fa[od[i]]]+=f[od[i]];
    for(int i=1;i<=cnt;i++)
    {
        ans+=(LL)w[fa[i]]*f[i]*l[fa[i]];
        w[fa[i]]+=f[i];
    }
    return ans;
}
char s[maxn];
int main()
{
    scanf("%s",s);
    n=strlen(s);
    for(int i=n-1;i>=0;i--) extend(s[i]-‘a‘);
    LL ans=0;
    for(int i=1;i<=n;i++) ans+=(LL)i*(n-1);
    printf("%lld\n",ans-2*solve());
    return 0;
}

另外转一下hzwer的SA解法:

--------------------------------------------------------------------------------------------------------------------------------------------蒟蒻与神犇的分界线--------------------------------------------

显然后缀数组不是正确姿势。。。

不过还是说说后缀数组的做法吧,bzoj总时限20s是能过的

SA+rmq求lcp应该烂大街了,这题还不用rmq。。。

首先求出h数组

考虑h[i]在哪些区间内会成为最小值,这个用两次单调栈很容易就能解决

还要处理一下由于h[i]可能相同造成的重复计数问题,具体看代码

code O(nlogn) 13592ms

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 500005
#define inf 1000000000
#define pa pair<int,int>
#define ll long long
using namespace std;
ll ans;
int n,k,p,q=1,top;
int v[N],a[N],h[N],sa[2][N],rk[2][N];
int st[N],l[N],r[N];
char ch[N];
void mul(int *sa,int *rk,int *SA,int *RK)
{
    for(int i=1;i<=n;i++)v[rk[sa[i]]]=i;
    for(int i=n;i;i--)
        if(sa[i]>k)
            SA[v[rk[sa[i]-k]]--]=sa[i]-k;
    for(int i=n-k+1;i<=n;i++)SA[v[rk[i]]--]=i;
    for(int i=1;i<=n;i++)
        RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i-1]]!=rk[SA[i]]||rk[SA[i-1]+k]!=rk[SA[i]+k]);
}
void presa()
{
    for(int i=1;i<=n;i++)v[a[i]]++;
    for(int i=1;i<=30;i++)v[i]+=v[i-1];
    for(int i=1;i<=n;i++)sa[p][v[a[i]]--]=i;
    for(int i=1;i<=n;i++)
        rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i-1]]!=a[sa[p][i]]);
    for(k=1;k<n;k<<=1,swap(p,q))
        mul(sa[p],rk[p],sa[q],rk[q]);
    for(int k=0,i=1;i<=n;i++)
    {
        int j=sa[p][rk[p][i]-1];
        while(ch[j+k]==ch[i+k])k++;
        h[rk[p][i]]=k;if(k>0)k--;
    }
}
void solve()
{
    for(int i=1;i<=n;i++)ans+=(ll)i*(n-1);
    h[0]=-inf;
    for(int i=1;i<=n;i++)
    {
        while(h[i]<=h[st[top]])top--;
        if(st[top]==0)l[i]=1;
        else l[i]=st[top]+1;
        st[++top]=i;
    }
    h[n+1]=-inf;top=0;st[0]=n+1;
    for(int i=n;i;i--)
    {
        while(h[i]<h[st[top]])top--;
        if(st[top]==n+1)r[i]=n;
        else r[i]=st[top]-1;
        st[++top]=i;
    }
    for(int i=1;i<=n;i++)
        ans-=2LL*(i-l[i]+1)*(r[i]-i+1)*h[i];
}
int main()
{
    scanf("%s",ch+1);
    n=strlen(ch+1);
    for(int i=1;i<=n;i++)a[i]=ch[i]-‘a‘+1;
    presa();
    solve();
    printf("%lld",ans);
    return 0;
}

时间: 2024-11-01 17:53:46

BZOJ3238: [Ahoi2013]差异 (后缀自动机)的相关文章

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

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]差异 后缀自动机 树形dp

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

【BZOJ3238】【Ahoi2013】差异 后缀自动机

转载请注明出处谢谢..http://blog.csdn.net/vmurder/article/details/42721101 首先 秦神QY Orz 题解: 这道题后缀数组过于鬼畜(wo'tai'ruo'bu'gan'xie) 所以写了简单好写易于理解不用分治不用RMQ的SAM大叔. 题解: 首先其实我们需要一个后缀树,然后两个后缀的lcp就是它们lca的len. 后缀树可以通过反序后缀自动机得到,这个很水. 然后len的性质就是后缀自动机的那个len(我写的'deep'). 后缀树上DP就

BZOJ3238: [Ahoi2013]差异

3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 437  Solved: 213[Submit][Status]Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacaoSample Output 54 HINT 2<=N<=500000,S由小写英文字母组成 题解: 刚看到题目,想着求出height然后搞出每个点作为最小值向右向左拓

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<

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