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>
#include <cstring>
#include <vector>
using namespace std;
const int N=1000010;
struct SAM{
    char s[N];
    int p,q,np,nq,cnt,lst,a[N][26],l[N],f[N],size[N],tot;
    int Tr(char c){return c-‘a‘;}
    SAM(){cnt=0;lst=++cnt;}
    void extend(int c){
        p=lst;np=lst=++cnt;l[np]=l[p]+1;size[np]=1;
        while(!a[p][c]&&p)a[p][c]=np,p=f[p];
        if(!p)f[np]=1;
        else{
            q=a[p][c];
            if(l[p]+1==l[q])f[np]=q;
            else{
                nq=++cnt;l[nq]=l[p]+1;
                memcpy(a[nq],a[q],sizeof(a[q]));
                f[nq]=f[q]; f[np]=f[q]=nq;
                while(a[p][c]==q)a[p][c]=nq,p=f[p];
            }
        }
    }
    vector<int> v[N];
    void BuildTree(){
        scanf("%s",s+1);
        int len=strlen(s+1);
        for(int i=len;i;i--)extend(Tr(s[i]));
        for(int i=2;i<=cnt;i++)v[f[i]].push_back(i);
    }
    long long res;
    void Dfs(int x,int fx){
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i];
            Dfs(y,x); size[x]+=size[y];
        }l[x]-=l[fx];
        res=res-(long long)size[x]*(size[x]-1)*l[x];
    }
    void ShowResult(){
        int len=strlen(s+1);
        res=(long long)(len-1)*len*(len+1)/2;
        for(int i=0;i<v[1].size();i++)Dfs(v[1][i],1);
        printf("%lld\n",res);
    }
}sam;
int main(){
    sam.BuildTree();
    sam.ShowResult();
    return 0;
}
时间: 2024-08-09 02:20:01

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

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 /********************************************************

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]差异 后缀数组 + 单调栈

题目链接 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得到后缀树

●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]差异

一看字符串 最长公共前缀,用后缀数组+单调栈搞搞就行啦.一定要注意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]差异【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 加上. 然后考虑一下减掉的是什么. 对于每个子