BZOJ3238 [Ahoi2013]差异

给定一个串,问其任意两个后缀的最长公共前缀长度的和

1.又是后缀,又是\(lcp\),很显然直接拿\(SA\)的\(height\)数组搞就好了,配合一下单调栈

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const int MAXN = 5e5+7;
struct SA{
int sa[MAXN],rk[MAXN],c[MAXN],sec[MAXN],height[MAXN],n;
char s[MAXN];
void getsa(int m){
n = strlen(s+1);
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rk[i]=s[i]]++;
for(int i = 1; i <= m; i++) c[i] += c[i-1];
for(int i = n; i >= 1; i--) sa[c[rk[i]]--] = i;
for(int k = 1; k <= n; k <<= 1){
int p = 0;
for(int i = n - k + 1; i <= n; i++) sec[++p] = i;
for(int i = 1; i <= n; i++) if(sa[i]>k) sec[++p] = sa[i] - k;
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rk[sec[i]]]++;
for(int i = 1; i <= m; i++) c[i] += c[i-1];
for(int i = n; i >= 1; i--) sa[c[rk[sec[i]]]--] = sec[i];
p = 0;
swap(rk,sec);
rk[sa[1]] = ++p;
for(int i = 2; i <= n; i++) rk[sa[i]] = sec[sa[i]]==sec[sa[i-1]] and sec[sa[i]+k]==sec[sa[i-1]+k] ? p : ++p;
if(p==n) break;
m = p;
}
}
void getheight(){
int k = 0;
for(int i = 1; i <= n; i++){
if(k) k--;
int j = sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rk[i]] = k;
}
}
LL solve(){
getsa(128);
getheight();
LL ret = (n+1ll) * n * (n-1ll) / 2;
stack<pair<int,int> > stk;
LL tot = 0;
for(int i = 1; i <= n; i++){
int num = 1;
while(!stk.empty() and height[i]<=height[stk.top().first]){
num += stk.top().second;
tot -= 1ll * stk.top().second * (height[stk.top().first] - height[i]);
stk.pop();
}
stk.push(make_pair(i,num));
tot += height[i];
ret -= tot * 2;
}
return ret;
}
}sa;
char s[MAXN];
int main(){
scanf("%s",sa.s+1);
printf("%lld\n",sa.solve());
return 0;
}

2.考虑用\(SAM\)来做,把串反转一下,现在要计算的是任意两个前缀的最长公共后缀的和

\(SAM\)有个性质,那就是两个点的\(LCA\)的状态表示的最长串,是这两个点表示的子串的最长公共后缀

所以我们可以枚举所有的\(LCA\),然后计算每一个\(LCA\)的贡献,先计算其任意两棵子树对应的贡献,再计算子树和当前节点产生的贡献

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
using LL = int_fast64_t;
const int MAXN = 1e6+7;
struct SAM{
int len[MAXN],link[MAXN],ch[MAXN][26],cnt[MAXN],tot,last;
vector<int> G[MAXN];
SAM(){ link[0] = -1; }
void extend(int c){
int np = ++tot, p = last;
len[np] = len[last] + 1; cnt[np] = 1;
while(p!=-1 and !ch[p][c]){
ch[p][c] = np;
p = link[p];
}
if(p==-1) link[np] = 0;
else{
int q = ch[p][c];
if(len[p]+1==len[q]) link[np] = q;
else{
int clone = ++tot;
len[clone] = len[p] + 1;
link[clone] = link[q];
memcpy(ch[clone],ch[q],sizeof(ch[q]));
link[np] = link[q] = clone;
while(p!=-1 and ch[p][c]==q){
ch[p][c] = clone;
p = link[p];
}
}
}
last = np;
}
void dfs(int u, LL &ret){
int all = 0;
for(int i = 0; i < (int)G[u].size(); i++){
int v = G[u][i];
dfs(v,ret);
all += cnt[v];
}
for(int i = 0; i < (int)G[u].size(); i++){
int v = G[u][i];
ret -= 1ll * (all - cnt[v]) * cnt[v] * len[u];
}
ret -= 2ll * all * cnt[u] * len[u];
cnt[u] += all;
}
void solve(char *s){
int n = strlen(s);
for(int i = 0; i < n; i++) extend(s[i]-'a');
for(int i = 1; i <= tot; i++) G[link[i]].push_back(i);
LL ret = n * (n-1ll) * (n+1ll) / 2;
dfs(0,ret);
printf("%lld\n",ret);
}
}sam;
char s[MAXN];
int main(){
scanf("%s",s);
sam.solve(s);
return 0;
}

BZOJ3238 [Ahoi2013]差异 【SAM or SA】的更多相关文章

  1. BZOJ3238:[AHOI2013]差异(SAM)

    Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N< ...

  2. bzoj3238 [Ahoi2013]差异 后缀数组+单调栈

    [bzoj3238][Ahoi2013]差异 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Ou ...

  3. [bzoj3238][Ahoi2013]差异_后缀数组_单调栈

    差异 bzoj-3238 Ahoi-2013 题目大意:求任意两个后缀之间的$LCP$的和. 注释:$1\le length \le 5\cdot 10^5$. 想法: 两个后缀之间的$LCP$和显然 ...

  4. luogu P4248 [AHOI2013]差异 SAM

    luogu P4248 [AHOI2013]差异 链接 luogu 思路 \(\sum\limits_{1<=i<j<=n}{{len}(T_i)+{len}(T_j)-2*{lcp ...

  5. BZOJ3238 [Ahoi2013]差异 SA+单调栈

    题面 戳这里 题解 考虑把要求的那个东西拆开算,前面一个东西像想怎么算怎么算,后面那个东西在建出\(height\)数组后相当于是求所有区间\(min\)的和*2,单调栈维护一波即可. #includ ...

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

    Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N< ...

  7. [BZOJ3238][AHOI2013]差异(后缀数组)

    求和式的前两项可以直接算,问题是对于每对i,j计算LCP. 一个比较显然的性质是,LCP(i,j)是h[rk[i]+1~rk[j]]中的最小值. 从h的每个元素角度考虑,就是对每个h计算有多少对i,j ...

  8. [BZOJ3238][Ahoi2013]差异解题报告|后缀数组

    Description 先分析一下题目,我们显然可以直接算出sigma(len[Ti]+len[Tj])的值=(n-1)*n*(n+1)/2 接着就要去算这个字符串中所有后缀的两两最长公共前缀总和 首 ...

  9. BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】

    题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...

随机推荐

  1. 跨域的几种方式以及call(),apply() bind()方法的作用和区别

    jsonp: jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案,是一种依靠开发人员创造出的一种非官方跨域数据交互协议. 一个是描述信息的格式,一个是信息传递 ...

  2. JVM-Class文件的结构

    Class类文件的结构 Class文件是一株以8个字节为单位的二进制流.各个数据项目严格按照顺序紧凑的排列在文件之中,中间没有任何的分隔符,当遇到占用的空间大于8个字节时,会按照高位在前的方式进行分割 ...

  3. Python运维自动化psutil 模块详解(超级详细)

    psutil 模块 参考官方文档:https://pypi.org/project/psutil/ 一.psutil简介 psutil是一个开源且跨平台(http://code.google.com/ ...

  4. 【Linux】同时插入多行数据到文本文件中

    如果想同时插入多行数据到指定的文本中,可以用下面的命令EOF cat >> test.txt <<EOF 1234 5678 GOOD EOF 将上述3行插入到 test.tx ...

  5. 【Linux】CentOS7中yumbackend.py进程的结束方法

    环境: CentOS Linux release 7.3.1611 (Core) 今天启动这个不怎么用的机器,才启动,就发现后台的yum无法进行安装,持续报这个错误 Loaded plugins: f ...

  6. SQL Server management studio使用sa连接时报错与伺服器的连接已成功,但在登入程序是发生错误

    使用Sql Server management studio的sa用户连接数据库时,报如下错误 解决方法: 1.使用windows验证登录 2.右键点击连接,点击属性,点击安全性,选择混合验证 3.重 ...

  7. 创建Django REST framework工程

    1.创建工程虚拟环境 2.创建工程目录和调整目录结构: 创建Django的项目 创建docs 用于存放一些说明文档资料 创建scripts 用于存放管理脚本文件 创建logs 用于存在日志 在与项目同 ...

  8. [USACO13DEC]牛奶调度Milk Scheduling

    原题链接https://www.lydsy.com/JudgeOnline/problem.php?id=4096 容易想到的一个测略就是,优先考虑结束时间小的牛.所以我们对所有牛按照结束时间排序.然 ...

  9. UI测试框架

    1. 从上到下共分成4层: 用例层  组件管理层  元素管理层  公共数据层 2. 用例层: 将每条用例使用参数化, 公共参数存储到"公共数据层", 中间参数通过组件层传递 3. ...

  10. Django中多表关联的展示问题:

    增加一个知识点,当表中有多对多关联时,在前端展示的时候是一个列表,所以在展示的时候需要这样做: 表结构: class ProjectEnv(models.Model): project = model ...