题目链接 \(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. MyISAM索引和InnoDB索引的区别

    首先你要知道: 无论是Myisam和Innodb引擎,如果在建表的时候没有显示的定义一行主键列的话,他内部都会自动创建一个隐藏的主键索引: 主键索引以外的索引假设称为次索引:首先Myisam和Inno ...

  2. web font

    gfx.downloadable_fonts.enabled

  3. 洛谷-p2764(最小路径覆盖)(网络流24题)

    #include<iostream> #include<algorithm> #include<queue> #include<cstring> #in ...

  4. 51-nod(1443)(最短路)

    解题思路:最短路+记录前驱和,刚开始一直以为是最短路+MST,结果发现,因为无向图的原因,有些边权很小的边再最短路处理后可能这条边也符合某两个点的最短路径,所以我们觉得这条边也是可以在MST处理中使用 ...

  5. DRF 版本 认证

    DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢大家都知道我们开发项目是有多个版本的 当我们项目越来越更新~版本就越来越多我们不可能新的版本出了~以前旧的版本 ...

  6. kvm 一些web管理3方工具

    OpenNebula  Proxmox VE 

  7. 继承&派生 属性查找

    # 在单继承背景下,无论是新式类还是经典类属性查找顺序都一样 # 先object->类->父类->... 实例: class Foo: def f1(self): print('Fo ...

  8. WIN8.1下Prolific USB-to-Serial Comm Port驱动黄色感叹号问题

    文章解决来源:http://blog.csdn.net/gsj0791/article/details/17664861 在做fpga口的uart回环测试时候,由于开发板上的是usb转uart,所以需 ...

  9. Codeforces Round #555 (Div. 3)[1157]题解

    不得不说这场div3是真的出的好,算得上是从我开始打开始最有趣的一场div3.因为自己的号全都蓝了,然后就把不经常打比赛的dreagonm的号借来打这场,然后...比赛结束rank11(帮dreago ...

  10. bzoj3796(后缀数组)(SA四连)

    bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...