[洛谷P4248][AHOI2013]差异
题目大意:给一个长度为$n$的字符串,求:
$$
\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|-2\times lcp(suf_i,suf_j)
$$
题解:建一棵后缀树,这个式子就成了后缀树上所有后缀之间的距离(后缀树可以把字符串反着加入后缀自动机得到的$fail$数组而来),然后有两种做法:
1. 把$\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|$直接求出来
$$
\begin{align*}
  &\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|\\
  =&\sum\limits_{1\leqslant i<j\leqslant n}i+j\\
  =&\dfrac{n(n+1)(n-1)} 2
  \end{align*}
$$
然后对每个点考虑它作为$lca$的贡献
2. 直接考虑每条边的贡献
卡点:无
C++ Code:(方法一)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 500010 namespace SAM {
#define N (maxn << 1)
int head[N], cnt;
struct Edge {
int to, nxt;
} e[N];
inline void addedge(int a, int b) {
e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
} int R[N], fail[N], nxt[N][26];
int lst = 1, idx = 1;
int sz[N];
void append(char __ch) {
int ch = __ch - 'a';
int p = lst, np = lst = ++idx;
R[np] = R[p] + 1; sz[np] = 1;
for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np;
if (!p) fail[np] = 1;
else {
int q = nxt[p][ch];
if (R[q] == R[p] + 1) fail[np] = q;
else {
int nq = ++idx;
std::copy(nxt[q], nxt[q] + 26, nxt[nq]);
fail[nq] = fail[q], R[nq] = R[p] + 1, fail[np] = fail[q] = nq;
for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq;
}
}
} long long ans;
void dfs(int u) {
long long tmp = 0;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
dfs(v);
tmp += static_cast<long long> (sz[u]) * sz[v];
sz[u] += sz[v];
}
ans += 2 * tmp * R[u];
}
long long work() {
for (int i = 2; i <= idx; i++) addedge(fail[i], i);
dfs(1);
return ans;
}
#undef N
} int n;
char s[maxn];
long long ans;
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
for (int i = n; i; i--) SAM::append(s[i]);
ans = static_cast<long long> (n - 1) * n * (n + 1) / 2;
ans -= SAM::work();
printf("%lld\n", ans);
return 0;
}
C++ Code:(方法二)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 500010 long long ans;
int n;
namespace SAM {
#define N (maxn << 1)
int head[N], cnt;
struct Edge {
int to, nxt;
} e[N];
inline void addedge(int a, int b) {
e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
} int R[N], fail[N], nxt[N][26];
int lst = 1, idx = 1;
int sz[N];
void append(char __ch) {
int ch = __ch - 'a';
int p = lst, np = lst = ++idx;
R[np] = R[p] + 1; sz[np] = 1;
for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np;
if (!p) fail[np] = 1;
else {
int q = nxt[p][ch];
if (R[q] == R[p] + 1) fail[np] = q;
else {
int nq = ++idx;
std::copy(nxt[q], nxt[q] + 26, nxt[nq]);
fail[nq] = fail[q], R[nq] = R[p] + 1, fail[np] = fail[q] = nq;
for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq;
}
}
} void dfs(int u) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
dfs(v);
sz[u] += sz[v];
ans += static_cast<long long> (n - sz[v]) * (sz[v]) * (R[v] - R[u]);
}
}
void work() {
for (int i = 2; i <= idx; i++) addedge(fail[i], i);
dfs(1);
}
#undef N
} char s[maxn];
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
for (int i = n; i; i--) SAM::append(s[i]);
SAM::work();
printf("%lld\n", ans);
return 0;
}
[洛谷P4248][AHOI2013]差异的更多相关文章
- 洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)
		题目见此 题解:首先所有后缀都在最后一个np节点,然后他们都是从1号点出发沿一些字符边到达这个点的,所以下文称1号点为根节点,我们思考一下什么时候会产生lcp,显然是当他们从根节点开始一直跳相同节点的 ... 
- 洛谷4248 AHOI2013差异 (后缀数组SA+单调栈)
		补博客! 首先我们观察题目中给的那个求\(ans\)的方法,其实前两项没什么用处,直接\(for\)一遍就求得了 for (int i=1;i<=n;i++) ans=ans+i*(n-1); ... 
- 洛谷 P4248: bzoj 3238: [AHOI2013]差异
		题目传送门:洛谷 P4248. 题意简述: 定义两个字符串 \(S\) 和 \(T\) 的差异 \(\operatorname{diff}(S,T)\) 为这两个串的长度之和减去两倍的这两个串的最长公 ... 
- P4248 [AHOI2013]差异 解题报告
		P4248 [AHOI2013]差异 题目描述 给定一个长度为 \(n\) 的字符串 \(S\),令 \(T_i\) 表示它从第 \(i\) 个字符开始的后缀.求 \[\displaystyle \s ... 
- luogu P4248 [AHOI2013]差异 SAM
		luogu P4248 [AHOI2013]差异 链接 luogu 思路 \(\sum\limits_{1<=i<j<=n}{{len}(T_i)+{len}(T_j)-2*{lcp ... 
- 线段树分治初步学习&洛谷P5227[AHOI2013]连通图
		线段树分治 其实思想说起来是比较简单的,我们把这个题里的所有操作(比如连边删边查询balabala)全部拍到一棵线段树上,然后对着整棵树dfs一下求解答案,顺便把操作做一下,回溯的时候撤销一下即可.虽 ... 
- bzoj 3236: 洛谷 P4396: [AHOI2013]作业 (莫队, 分块)
		题目传送门:洛谷P4396. 题意简述: 给定一个长度为\(n\)的数列.有\(m\)次询问,每次询问区间\([l,r]\)中数值在\([a,b]\)之间的数的个数,和数值在\([a,b]\)之间的不 ... 
- 洛谷 P4248 / loj 2377 [AHOI2013] 差异 题解【后缀自动机】【树形DP】
		可能是一个 SAM 常用技巧?感觉 SAM 的基础题好多啊.. 题目描述 给定一个长度为 \(n\) 的字符串 \(S\) ,令 \(T_i\) 表示它从第 \(i\) 个字符开始的后缀,求: \[ ... 
- 【洛谷 P4248】 [AHOI2013]差异(后缀自动机)
		题目链接 \[ans=\sum_{1<=i<j<=n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\] 观察这个式子可以发现,前面两个\(len\)是常数,后面的 ... 
随机推荐
- #386. 【UNR #3】鸽子固定器
			#386. [UNR #3]鸽子固定器 题目链接 官方题解 分析: 神奇的做法+链表. 首先按照大小排序. 对于小于选择小于m个物品的时候,这个m个物品一定是一段连续的区间.因为,如果中间空着一个物品 ... 
- 水灾 1000MS  64MB (广搜)
			水灾(sliker.cpp/c/pas) 1000MS 64MB 大雨应经下了几天雨,却还是没有停的样子.土豪CCY刚从外地赚完1e元回来,知道不久除了自己别墅,其他的地方都将会被洪水淹没. CCY ... 
- getSteam
			using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ... 
- Oracle锁表处理
			最近系统连续出现好几次锁表,昨晚又发生一次锁表,11点钟跑到客户现场,进过跟踪发现导致这次锁表的机器和上一次是同一台,花了近半小时解锁.之后到科室找到那台机器看看情况,发现那台机器速度超慢,保存一份病 ... 
- 手机APP测试如何进行兼容性测试?
			Android App兼容性测试是一个比较重要的App评价内容,实际上兼容性测试不仅仅和测试人员相关,在开发阶段就应当着重考虑,因为兼容性问题是除了实现App本身要求的功能后,必须要关注.而且至关重要 ... 
- tpo-09 C1 Advice on a term paper's topic
			第 1 段 1.Listen to a conversation between a student and her professor. 请听一段一名学生和教授讨论的对话. 第 2 段 1.Befo ... 
- NGUI制作流光效果
			效果展示: 技巧: 1.勾选UIPanel下的Normal启用UI的法线贴图,并建立带有法线贴图的UI对象(此处用NGUI自带的Reflector.Atlas中的图作为UI). 2.建立点光源并为其添 ... 
- JAVA基础学习之路(六)数组与方法参数的传递
			通常,向方法中传递的都是基本数据类型,而向方法中传递数组时,就需要考虑内存的分配 public class test2 { public static void main(String args[]) ... 
- Python数据分析基础——Numpy tutorial
			参考link https://docs.scipy.org/doc/numpy-dev/user/quickstart.html 基础 Numpy主要用于处理多维数组,数组中元素通常是数字,索引值为 ... 
- java核心技术 笔记
			一 . 总览 1. 类加载机制:jdk内嵌的class_loader有哪些,类加载过程.--后面需要补充 2. 垃圾收集基本原理,常见的垃圾收集器,各自适用的场景.--后面需要补充 3. 运行时动态编 ... 
