题目大意:给一个长度为$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]差异的更多相关文章

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

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

  2. 洛谷4248 AHOI2013差异 (后缀数组SA+单调栈)

    补博客! 首先我们观察题目中给的那个求\(ans\)的方法,其实前两项没什么用处,直接\(for\)一遍就求得了 for (int i=1;i<=n;i++) ans=ans+i*(n-1); ...

  3. 洛谷 P4248: bzoj 3238: [AHOI2013]差异

    题目传送门:洛谷 P4248. 题意简述: 定义两个字符串 \(S\) 和 \(T\) 的差异 \(\operatorname{diff}(S,T)\) 为这两个串的长度之和减去两倍的这两个串的最长公 ...

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

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

  5. luogu P4248 [AHOI2013]差异 SAM

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

  6. 线段树分治初步学习&洛谷P5227[AHOI2013]连通图

    线段树分治 其实思想说起来是比较简单的,我们把这个题里的所有操作(比如连边删边查询balabala)全部拍到一棵线段树上,然后对着整棵树dfs一下求解答案,顺便把操作做一下,回溯的时候撤销一下即可.虽 ...

  7. bzoj 3236: 洛谷 P4396: [AHOI2013]作业 (莫队, 分块)

    题目传送门:洛谷P4396. 题意简述: 给定一个长度为\(n\)的数列.有\(m\)次询问,每次询问区间\([l,r]\)中数值在\([a,b]\)之间的数的个数,和数值在\([a,b]\)之间的不 ...

  8. 洛谷 P4248 / loj 2377 [AHOI2013] 差异 题解【后缀自动机】【树形DP】

    可能是一个 SAM 常用技巧?感觉 SAM 的基础题好多啊.. 题目描述 给定一个长度为 \(n\) 的字符串 \(S\) ,令 \(T_i\) 表示它从第 \(i\) 个字符开始的后缀,求: \[ ...

  9. 【洛谷 P4248】 [AHOI2013]差异(后缀自动机)

    题目链接 \[ans=\sum_{1<=i<j<=n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\] 观察这个式子可以发现,前面两个\(len\)是常数,后面的 ...

随机推荐

  1. MySQL高级-锁机制

    一.概述 1.定义 2.锁的分类 ①从对数据操作的类型(读\写)分 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响. 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁 ...

  2. php post

    post form function post($remote_server,$data,$second=60){ $ch = curl_init();if(is_string($data)){ $t ...

  3. 2019年猪年海报PSD模板-第四部分

    14套精美猪年海报,免费猪年海报,下载地址:百度网盘,https://pan.baidu.com/s/1WUO4L5PHIHG5hAurv52_2A                        

  4. hdu2509Be the Winner(反nim博弈)

    Be the Winner Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  5. ISE 14.7安装教程最新版(Win10安装)——解决Win10安装完后打不开快捷方式的方法

    ISE 14.7安装教程最新版(Win10安装) Xilinx ISE是一款世界著名的硬件设计软件,它为设计流程的每一步都提供了直观的生产力增强工具,覆盖从系统级设计探索.软件开发和基于HDL硬件设计 ...

  6. TW实习日记:第13天

    昨天困扰的问题终于解决了.因为是百度地图api提供的函数,所以这个解决办法并不适用于所有异步请求,仅仅针对百度地图api的调用接口函数和回调函数.有两种解决方法可以解决百度地图api中常出现的请求回调 ...

  7. 油田 (Oil Deposits UVA - 572)

    题目描述: 原题:https://vjudge.net/problem/UVA-572 题目思路: 1.图的DFS遍历 2.二重循环找到相邻的八个格子 AC代码: #include <iostr ...

  8. LeetCode 135——分发糖果

    1. 题目 2. 解答 初始化左序奖赏全为 1,从左往右遍历,如果右边的人评分比左边高,右边奖赏比左边奖赏增 1. 初始化右序奖赏全为 1,从右往左遍历,如果左边的人评分比右边高,左边奖赏比右边奖赏增 ...

  9. HADOOP docker(五):hadoop用户代理 Proxy user

    1.hadoop用户代理简介2.配置3.实验 1.hadoop用户代理简介 hadoop用户代理功能的作用是让超级用户superuser模拟一个普通用户来执行任务.比如用户joe通过oozie提交一个 ...

  10. Python高级编程-使用SQLite

    SQLite是一种嵌入式数据库,它的数据库就是一个文件.由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成. Python就 ...