[洛谷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\)是常数,后面的 ...
随机推荐
- Java:List判空的条件:List=null 和 List.size = 0
当需要对一个LIst进行判空操作时我们可使用如下两个语句: if (list == null || list.size() == 0) {} if (list != null && l ...
- 创龙DSP6748学习之RS485收发
1. 先看下原理图,第一个问题,RS485其实就是使用的串口USART1,同时485的输出脚之间接120欧姆的电阻. 遇到个问题,为什么有两个使能引脚?还有RS485_A和RS485_B为什么分别接上 ...
- 鸡啄米:C++编程之十四学习之构造函数和析构函数
1. 本人学习鸡啄米课程的笔记记录,用来记录学习的历程和进度 2. 构造函数 我们在声明一个变量时,如果对它进行了初始化,那么在为此变量分配内存空间时还会向内存单元中写入变量的初始化.声明对象有相似的 ...
- 关于iOS和Android的安装包更新笔记
关于iOS和Android的安装包更新问题 1. Android更新apk 1)使用DownloadManager下载 2)使用HttpClient下载 apk的下载不能使用ssl,即不能使用http ...
- Python对象引用问题总结
对于对象引用问题,一直是一知半解的状态,现整理以备使用. 操作不可变对象进行加减运算时,会在内存中创建新的不可变实例,不会影响原来的引用>>> c=12>>> d= ...
- photoshop cc 2018安装破解教程(破解补丁,亲测,绝对可用)
破解步骤说明:下载地址百度网盘,https://pan.baidu.com/s/1cWtpUesl2fms3tFwEC0MiQ 1.右键解压Adobe Photoshop CC 2018 64位这个文 ...
- 汽车VIN码,车架号,移动端,服务器端OCR识别 技术公司
很多人在购买车辆的时候,只关注性能.外观.内饰等,其实真正的内行是首先看车辆的VIN码,也叫车架号码. VIN码(车架号码)是一辆车的唯一身份证明,一般在车辆的挡风玻璃处,有的在车辆防火墙上,或B柱铭 ...
- Selenium 入门到精通系列:二
Selenium 入门到精通系列 PS:用户登录 例子 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2019-04-23 16:12 ...
- Solr与Elasticsearch区别
Elasticsearch Elasticsearch是一个实时的分布式搜索和分析引擎.它可以帮助你用前所未有的速度去处理大规模数据. 它可以用于全文搜索,结构化搜索以及分析. 优点 Elastics ...
- 【WXS】变量定义保留标识符
以下字符不能作为变量名称定义: delete void typeof null undefined NaN Infinity var if else true false require this f ...