题目链接 \(Click\) \(Here\)

神仙题。或者可能我太菜了没见过后缀数组的骚操作,然后就被秀了一脸\(hhhhh\)

$$\sum\limits_{1<=i < j <= n} len(T_i) + len(T_j) - 2 * lcp (T_i, T_j)$$

这个式子我们显然可以把前面拆出来当常数算(\({(n - 1) * n * (n + 1) }/ 2\)),剩下的就是怎么计算每个区间的\(lcp\)之和了。

这个问题,我们转化成后缀数组的\(height\)来进行计算。仔细思考会发现,原字符串的每对\(i\)和\(j\)事实上和\(height\)数组的每一段区间\([rk[i], rk[j]]\)一一对应。至此,我们的问题又转化成了求\(height\)数组上的每一个区间的最小值之和。

暴力求显然是\(O(N^2)\)的,承受不住。根据\(lcp\)具有可合并性$ min(lcp (T_i,T_ j), lcp (T_{j + 1}, T_{k})) = lcp (T_i, T_k)\(,而\)height\(数组又代表了\)lcp(T_i, T_{sa[rk[i ] - 1]})$,那么我们就可以这么做:

  • 设\(dp[i]\)为\(height\)数组中前缀\(i\)的每一个后缀贡献出的答案。

  • 对于任意\(i > p\),当\(height[i] >= height[p]\)时,我们可以在所有\(height[p]\)统治的答案里,在后面缀上一个\([p-1,i]\)的区间,所以可以认为是:前缀\(i\)的贡献中,还要包含一个前缀\(p\)的总贡献。

  • 所以只要计算最近的一个\(p\)就可以囊括\([1,p]\)内的所有答案,维护最近的小于\(height[i]\)的\(p\)的位置即可。

  • 所以有\(f[i] = f[p] + (i - p) * height[i];\)

  • 如果\(i\)的前面不存在\(p\),满足\(height[p] <= height[i]\),那么前缀\([1,i]\)的所有后缀\(height\)的最小值都是\(height[i]\)(被\(i\)统治),即贡献为\(i * height[i]\)。

为了维护前一个比\(height[i]\)小的\(height\)值的相关信息,我们需要开一个递增的单调栈,遍历到\(i\)时弹出所有\(height\)值小于\(height[i]\)的元素,结束时再插入该\(height\)。

#include <bits/stdc++.h>
using namespace std; #define LL long long
const int N = 500010; char s[N];
int n, m = 255, sa[N], tp[N];
int rk[N], _rk[N], bin[N], height[N]; void base_sort () {
for (int i = 0; i <= m; ++i) bin[i] = 0;
for (int i = 1; i <= n; ++i) bin[rk[tp[i]]]++;
for (int i = 1; i <= m; ++i) bin[i] += bin[i - 1];
for (int i = n; i >= 1; --i) sa[bin[rk[tp[i]]]--] = tp[i];
} void suffix_sort () {
for (int i = 1; i <= n; ++i) {
tp[i] = i;
rk[i] = s[i - 1];
}
base_sort ();
for (int w = 1; w <= n; w <<= 1) {
int cnt = 0;
for (int i = n - w + 1; i <= n; ++i) {
tp[++cnt] = i;
}
for (int i = 1; i <= n; ++i) {
if (sa[i] > w) {
tp[++cnt] = sa[i] - w;
}
}
base_sort ();
memcpy (_rk, rk, sizeof (rk));
rk[sa[1]] = cnt = 1;
for (int i = 2; i <= n; ++i) {
rk[sa[i]] = _rk[sa[i]] == _rk[sa[i - 1]] && _rk[sa[i] + w] == _rk[sa[i - 1] + w] ? cnt : ++cnt;
}
if (cnt == n) break;
m = cnt;
}
// printf ("sa : ");for (int i = 1; i <= n; ++i) printf ("%d ", sa[i]); printf ("\n");
} void get_height () {
int k = 0;
for (int i = 1; i <= n; ++i) {
if (k) k--;
int j = sa[rk[i] - 1];
while (s[i + k - 1] == s[j + k - 1]) ++k;
height[rk[i]] = k;
}
// printf ("height : ");
// for (int i = 1; i <= n; ++i) {
// printf ("%d ", height[i]);
// }
// printf ("\n");
} struct node {
int pos, val;
node (int ppos = 0, int vval = 0) {pos = ppos, val = vval;}
}; node sta[N]; int top;
LL f[N]; int main () {
scanf ("%s", s);
n = strlen (s);
suffix_sort ();
get_height ();
for (int i = 1; i <= n; ++i) {
while (top > 0 && sta[top].val > height[i]) --top;
//使sta[top].val <= height[i];
if (top > 0) {
int p = sta[top].pos; //p记录控制范围
f[i] = f[p] + (i - p) * height[i];
} else {
f[i] = i * height[i];
}
sta[++top] = node (i, height[i]);
}
LL ans = 1LL * (n - 1) * n * (n + 1) / 2;
for (int i = 1; i <= n; ++i) {
ans -= 2 * f[i];
}
cout << ans << endl;
}

Luogu P4248 [AHOI2013]差异的更多相关文章

  1. luogu P4248 [AHOI2013]差异 SAM

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

  2. P4248 [AHOI2013]差异 解题报告

    P4248 [AHOI2013]差异 题目描述 给定一个长度为 \(n\) 的字符串 \(S\),令 \(T_i\) 表示它从第 \(i\) 个字符开始的后缀.求 \[\displaystyle \s ...

  3. P4248 [AHOI2013]差异

    思路 SAM 后缀自动机parent树的LCA就是两个子串的最长公共后缀 现在要求LCP 所以把字符串反转一下 然后每个点的贡献就是endpos的大小,dfs一遍求出贡献就可以了 代码 #includ ...

  4. 洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)

    题目见此 题解:首先所有后缀都在最后一个np节点,然后他们都是从1号点出发沿一些字符边到达这个点的,所以下文称1号点为根节点,我们思考一下什么时候会产生lcp,显然是当他们从根节点开始一直跳相同节点的 ...

  5. [洛谷P4248][AHOI2013]差异

    题目大意:给一个长度为$n$的字符串,求: $$\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|-2\times lcp(suf_i, ...

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

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

  7. bzoj 3238 Ahoi2013 差异

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

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

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

  9. BZOJ_3238_[Ahoi2013]差异_后缀自动机

    BZOJ_3238_[Ahoi2013]差异_后缀自动机 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sam ...

随机推荐

  1. Python——FTP上传和下载

    一.FTP对象方法说明 login(user='anonymous',passwd='', acct='') 登录 FTP 服务器,所有参数都是可选的 pwd() 获得当前工作目录 cwd(path) ...

  2. 学习 Spring (四) Bean 的生命周期

    Spring入门篇 学习笔记 定义 --> 初始化 --> 使用 --> 销毁 初始化 实现 org.springframework.beans.factory.Initializi ...

  3. 原子变量与CAS算法(二)

    一.锁机制存在的问题 (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2)一个线程持有锁会导致其它所有需要此锁的线程挂起. (3)如果一个优先级高的线程等待一个 ...

  4. vue的 v-for 循环中图片加载路径问题

    先看一下产品需求,如下图所示, 产品要求图片和它的名称一一对应,本来是非常简单的需求,后台直接返回图片路径和名称,前台直接读取就可以了,但是我们没有存储图片的服务器,再加上是一个实验性的需求,图片需要 ...

  5. Codeforces 888G(分治+trie)

    按位贪心,以当前考虑位是0还是1将数分成两部分,则MST中这两部分之间只会存在一条边,因为一旦有两条或以上的边,考虑两条边在原图中所成的环,显然这两条边有一条是环上的权值最大边,不会出现在MST中.则 ...

  6. OpenCv的CV2一些函数总结

  7. win10系统同时安装python2.7和python3.6

    我是先在本机上安装的python3.6.5,因为要学习一个框架,但是这个框架只支持python2,所以我又安装了python2.7.15,并且配置到系统环境变量 环境变量配置了python3.6.5的 ...

  8. Quartus prime 16.0 signaltap II 使用

    前言 由于逻辑分析仪太贵,altera贴心提供signal tap II来观察输出波形,不过使能signaltap II会占用片内ram,毕竟原理就是把数据采样到ram中再通过jtag口上传到quar ...

  9. 【HDU - 5790 】Prefix(主席树+Trie树)

    BUPT2017 wintertraining(15) #7C 题意 求[min((Z+L)%N,(Z+R)%N)+1,max((Z+L)%N,(Z+R)%N)+1]中不同前缀的个数,Z是上次询问的结 ...

  10. 通过shell调用rtx接口

    本脚本可获取服务器Site值和服务器ip,执行之后可通过RTX推送系统消息 脚本例子如下: #!/bin/bash function alarm(){ user="$1" cont ...