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. 经典项目管理 OR 敏捷项目管理,我该怎么选?

    CODING 项目协同近期为支持传统项目管理推出了「经典项目管理」.至此,CODING 已全面支持敏捷项目管理以及传统项目管理.那么问题来了,「经典项目管理」和「敏捷项目管理」,我该怎么选呢?本文将从 ...

  2. Head First 设计模式 —— 09. 模版方法 (Template Method) 模式

    模板方法模式 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. P289 特点 主导算法框架,并且保护这个算法 P28 ...

  3. 03--Docker 容器和镜像常用命令

    一.帮助命令 docker version docker info docker --help =====================镜像命令=========================== ...

  4. [Usaco2008 Open]Roads Around The Farm分岔路口

    题目描述 约翰的N(1≤N≤1,000,000,000)只奶牛要出发去探索牧场四周的土地.她们将沿着一条路走,一直走到三岔路口(可以认为所有的路口都是这样的).这时候,这一群奶牛可能会分成两群,分别沿 ...

  5. Spring Bean详解

    Spring Bean 在Spring的应用中,Spring IoC容器可以创建.装配和配置应用组件对象,这里的组件对象称为Bean. Bean的配置 Spring可以看作一个大型工厂,用于生产和管理 ...

  6. uni-app开发经验分享十三:实现手机扫描二维码并跳转全过程

    最近使用 uni-app 开发 app ,需要实现一个调起手机摄像头扫描二维码功能,官网API文档给出了这样一个demo: // 允许从相机和相册扫码 uni.scanCode({ success: ...

  7. Redis 实战 —— 06. 持久化选项

    持久化选项简介 P61 Redis 提供了两种不同的持久化方法来将数据存储到硬盘里面. RDB(redis database):可以将某一时刻的所有数据都写入硬盘里面.(保存的是数据本身) AOF(a ...

  8. OpenStack各组件的常用命令

    openstack命令 openstack-service restart    #重启openstack服务 openstack endpoint-list        #查看openstack的 ...

  9. 在原生开发中控制HTML5视频!

    在原生开发中控制HTML5视频! PC端 视频如何自动播放! 在video标签中添加 autoplay + muted(静音属性!) 温馨提醒: video是一个块级元素! 但是唯一的缺陷就是视频没有 ...

  10. jQuery 移入显示div,移出当前div,移入到另一个div还是显示。

    jQuery 移入移出 操作div 1 <style type="text/css"> 2 .box{ 3 position: relative; 4 } 5 .box ...