[传送门[(https://www.lydsy.com/JudgeOnline/problem.php?id=3238)

解题思路

  首先原式可以把\(len\)那部分直接算出来,然后通过后缀数组求\(lcp\)。算\(\sum lcp\)的时候,刚开始傻了想要直接算贡献,结果越写越乱,后来想想只需要用单调栈把每个点的控制范围算出来即可,正着做一遍反着做一遍。注意还要考虑两个\(h[i]\)相邻并相等时的影响。还有一种比较自然的解法是后缀树,\(lcp\)其实就为两个点的\(lca\)的深度,所以建出后缀树后直接按拓扑序\(dp\)一下即可。

代码

后缀数组:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib> using namespace std;
const int MAXN = 500005;
typedef long long LL; int n,m,height[MAXN],num,stk[MAXN],top,l[MAXN],r[MAXN];
int sa[MAXN],rk[MAXN],x[MAXN<<1],y[MAXN<<1],c[MAXN];
char s[MAXN];
LL ans; inline void get_SA(){
for(int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
for(int i=2;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){num=0;
for(int i=n-k+1;i<=n;i++) y[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=2;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);num=1;x[sa[1]]=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==n) break;m=num;
}
} inline void get_height(){
for(int i=1;i<=n;i++) rk[sa[i]]=i;int j,k=0;
for(int i=1;i<=n;i++){
if(rk[i]==1) continue;
if(k) k--;j=sa[rk[i]-1];
while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
} void solve(){
for(int i=1;i<=n;i++){
while(top && height[i]<=height[stk[top]]) l[stk[top]]=i-1,top--;
if(height[i]) stk[++top]=i;
}
while(top) l[stk[top--]]=n;
for(int i=n;i;i--){
while(top && height[i]<height[stk[top]]) r[stk[top]]=i+1,top--;
if(height[i]) stk[++top]=i;
}
while(top) r[stk[top--]]=1;
for(int i=1;i<=n;i++) ans-=(LL)height[i]*(l[i]-i+1)*(i-r[i]+1)*2;
} int main(){
scanf("%s",s+1);n=strlen(s+1);m='z';
get_SA();get_height();
// for(int i=1;i<=n;i++) cout<<sa[i]<<" ";cout<<endl;
// for(int i=1;i<=n;i++) cout<<height[i]<<" ";cout<<endl;
ans=(LL)n*(n-1)/2*(n+1);
solve();printf("%lld\n",ans);
return 0;
}

后缀树:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define int long long using namespace std;
const int MAXN = 500005<<1;
typedef long long LL; char s[MAXN];
int n,siz[MAXN],lst,cnt;
int fa[MAXN],ch[MAXN][27],l[MAXN],a[MAXN],c[MAXN];
LL ans; inline void Insert(int c){
int p=lst,np=++cnt;lst=np;l[np]=l[p]+1;
for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1) fa[np]=q;
else {
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
siz[np]=1;
} signed main(){
scanf("%s",s+1);n=strlen(s+1);lst=cnt=1;
for(int i=n;i;i--) Insert(s[i]-'a'+1);
ans=(LL)n*(n-1)/2*(n+1);
for(int i=1;i<=cnt;i++) c[l[i]]++;
for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
for(int i=1;i<=cnt;i++) a[c[l[i]]--]=i;
for(int i=cnt;i;i--){
ans-=(LL)siz[a[i]]*siz[fa[a[i]]]*l[fa[a[i]]]*2;
siz[fa[a[i]]]+=siz[a[i]];
}
printf("%lld\n",ans);
return 0;
}

BZOJ 3238: [Ahoi2013]差异((单调栈+后缀数组)/(后缀树))的更多相关文章

  1. BZOJ 3238: [Ahoi2013]差异 [后缀数组 单调栈]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2326  Solved: 1054[Submit][Status ...

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

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MB Description Input 一行,一个字符串S Output 一行,一个 ...

  3. BZOJ 3238: [Ahoi2013]差异 [后缀自动机]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2512  Solved: 1140[Submit][Status ...

  4. bzoj 3238 Ahoi2013 差异

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2357  Solved: 1067[Submit][Status ...

  5. BZOJ.3238.[AHOI2013]差异(后缀自动机 树形DP/后缀数组 单调栈)

    题目链接 \(Description\) \(Solution\) len(Ti)+len(Tj)可以直接算出来,每个小于n的长度会被计算n-1次. \[\sum_{i=1}^n\sum_{j=i+1 ...

  6. ●BZOJ 3238 [Ahoi2013]差异

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3238 题解: 后缀数组套路深. 问题转化为求出任意两个后缀的LCP之和 在计算贡献时,各种不 ...

  7. 洛谷 P4248: bzoj 3238: [AHOI2013]差异

    题目传送门:洛谷 P4248. 题意简述: 定义两个字符串 \(S\) 和 \(T\) 的差异 \(\operatorname{diff}(S,T)\) 为这两个串的长度之和减去两倍的这两个串的最长公 ...

  8. BZOJ 4826: [Hnoi2017]影魔 单调栈+可持久化线段树

    Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个 ...

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

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3238 [题目大意] 给出一个串,设T[i]表示从第i位开始的后缀, 求sum(len( ...

随机推荐

  1. MVC MVC3中 ViewBag、ViewData和TempData的使用和区别 【转】

    在MVC3开始,视图数据可以通过ViewBag属性访问,在MVC2中则是使用ViewData.MVC3中保留了ViewData的使用.ViewBag 是动态类型(dynamic),ViewData 是 ...

  2. H5页面前后端通信 (3种方式简单介绍)

    1.ajax:短连接 2.websocket :长连接,双向的.   node搭建的websocket服务器,推送信息给客户端浏览器 :https://www.cnblogs.com/fps2tao/ ...

  3. java中常用的转义字符(转)

    Java编程中往往需要一些特殊操作,例如空格,换行.或者一些你使用特殊符号的意愿与程序中特殊符号意思冲突的时候,我们不能直接写就需要把这些符号转义,表达你的本意,并与程序中特殊符号做区分,这些都需要转 ...

  4. Insmod模块加载过程分析

    一.背景 a) 在进行JZ2440的一个小demo开发的时候,使用自己编译的内核(3.4.2)及lcd模块进行加载时,insmod会提示加载失败因为内核版本不匹配(提示当前内核版本为空),并且显示模块 ...

  5. Android 测试点归纳总结

    前言 除了测试平台工具,业务测试的总结和思考同样重要,这里总结了一些Android测试知识点,可以辅助业务测试快速形成测试用例和检查点,当作抛砖引玉分享给大家.如有思考不全面的地方,欢迎大家指出来. ...

  6. gradle 排除jar

    排除fastjson的包,其他同理compile('com.qq.sdk:core:2.0.3') { exclude group: 'com.alibaba'}

  7. js千位符 | js 千位分隔符 | js 金额格式化

    js 千位分隔符 千位分隔符,其实就是数字中的逗号.依西方的习惯,人们在数字中加进一个符号,以免因数字位数太多而难以看出它的值.所以人们在数字中,每隔三位数加进一个逗号,也就是千位分隔符,以便更加容易 ...

  8. PAT甲级——A1149DangerousGoodsPackaging【25】

    When shipping goods with containers, we have to be careful not to pack some incompatible goods into ...

  9. 转 python3 读取 ini配置文件

    在代码中经常会通过ini文件来配置一些常修改的配置.下面通过一个实例来看下如何写入.读取ini配置文件. 需要的配置文件是: 1 [path] 2 back_dir = /Users/abc/Pych ...

  10. Linux NIO 系列(04-4) select、poll、epoll 对比

    目录 一.API 对比 1.1 select API 1.2 poll API 1.3 epoll API 二.总结 2.1 支持一个进程打开的 socket 描述符(FD)不受限制(仅受限于操作系统 ...