[传送门[(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. UNP学习 非阻塞I/O

    缺省状态下,套接口时阻塞方式的.这意味着当一个套接口调用不能立即完成时,进程进入睡眠状态,等待操作完成.我们将可能阻塞的套接口调用分成四种. 1.输入操作:read.readv.recv.recvfr ...

  2. 兼容新旧浏览器的flex写法

    拷贝直接加类名即可 /* 定义 */ .flex-def { display: -webkit-box; /* 老版本语法: Safari, iOS, Android browser, older W ...

  3. 团队冲刺DAY3

    DESUtil.java是包含加密解密抽象类的Java文件 DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准 ...

  4. HTML-参考手册: HTML 事件属性

    ylbtech-HTML-参考手册: HTML 事件属性 1.返回顶部 1. HTML 事件属性 全局事件属性 HTML 4 的新特性之一是可以使 HTML 事件触发浏览器中的行为,比方说当用户点击某 ...

  5. 使用 twine 上传自己的 python 包到 pypi

    打包检查 python setup.py check 打包 python3 setup.py sdist build 上传 twine upload dist/* twine 安装: pip inst ...

  6. PHP面试 MySQL的高可扩展和高可用

    MySQL的高可扩展和高可用 面试题一 MySQL分表和分区的工作原理,分表和分区的使用场景和优缺点. 分区表的原理 对用户而言,分区表时一个独立的逻辑表,但是底层MySQL将其分成了多个物理子表,这 ...

  7. Struts1.3——登录案例的改进

    在上篇写的用户登录验证案例的基础上进一步改进. 1.在登录成功后的welcome.jsp页面显示登录成功的用户名 思路1:在LoginAction中,将username放入request的域对象中 因 ...

  8. shell脚本之nginx的安装

           为了编写nginx自动部署的脚本而刚学习的shell脚本语言.写文章只是为了记录,有错误勿喷. 一.创建shell脚本程序        操作系统是Linux的 CentOS 7 版本. ...

  9. Reactor 反应堆设计模式

    为了应对高并发的服务器端开发,微软在2009年提出了一种更优雅地实现异步编程的方式Reactive Programming即反应式编程.随后其他技术紧随其后,比如ES6通过引入类似的异步编程方式等. ...

  10. 响应式web开发的一些文章

    CSS Device Adaptation:关注 W3C 建议的 CSS 设备适配标准. “在 CSS 中使用 LESS 实现更多的功能”(作者:Uche Ogbuji,developerWorks, ...