当前位置: 首页 > news >正文

8.22 万灵药(SAM + Trie + 树剖 + 线段树)

http://cplusoj.com/d/senior/p/479?tid=66c55d60c098fe0f6786d470

考虑如何求两个前缀的最长后缀

我们建一个SAM,把这两个前缀找出来,他们的公共后缀集合为这两个点在fail树上的公共祖先

那么最长后缀就是它们lca对应的最长串

接下来我们要统计这些串的前缀,首先肯定要拿一个trie来维护

直接建trie是 O ( n 2 ) O(n^2) O(n2) 的,但是这里有一个重要的性质,就是我们发现SAM节点和trie树节点是一一对应的

具体的,当前SAM节点在trie树上的父亲必然是某个走到当前节点的节点。我们只需要判断符合条件的len就行

因此我们现在把trie建出来了

我们现在要实现的功能是什么?

对于trie树,我们要查询一个点到根路径上所有值的权值,并对这条路径所有点的权值±1

这个东西直接剖就行

后面维护理论上要拿zkw或者树状数组才行,但我线段树冲过去了,复杂度是双log的

如果改成全局平衡二叉树,那就可以优化成单log了,黄队好像打了

my code:

#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL#define debug(...) fprintf(stdout, ##__VA_ARGS__)#define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else#define debug(...) void(0)#define debag(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
#define N 1000010
int n, m, i, j, k, T, ans; struct Binary_tree {int cnt[N]; void add(int x, int y) { if(x == 0) return cnt[x] += y, void(); while(x < N) cnt[x] += y, x += x & -x; }int qry(int x) {int ans = cnt[0]; while(x) ans += cnt[x], x -= x & -x; return ans; }
}Bin1, Bin2;struct Segment_tree {
#define ls (k << 1)
#define rs (k << 1 | 1)
#define mid ((l + r) >> 1)int tag[N << 2], s[N << 2]; void push_down(int k, int l, int r) {tag[ls] += tag[k]; tag[rs] += tag[k]; s[ls] += tag[k] * (mid - l + 1); s[rs] += tag[k] * (r - mid); assert(s[ls] + s[rs] == s[k]); tag[k] = 0; }void push_up(int k) { s[k] = s[ls] + s[rs]; }void add(int k, int l, int r, int x, int y, int v) {if(l >= x && r <= y) return tag[k] += v, s[k] += v * (r - l + 1), void(); push_down(k, l, r); if(x <= mid) add(ls, l, mid, x, y, v); if(y >= mid + 1) add(rs, mid + 1, r, x, y, v); push_up(k); }int qry(int k, int l, int r, int x, int y) {if(l >= x && r <= y) return s[k]; push_down(k, l, r); int ans = 0; if(x <= mid) ans += qry(ls, l, mid, x, y); if(y >= mid + 1) ans += qry(rs, mid + 1, r, x, y); 
//		debug("qry[%lld %lld] is %lld\n", )return ans; }
}Seg;struct Big_Binary_tree {void add(int x, int v) {Bin1.add(x, v); Bin2.add(x, v * x); }void add(int l, int r, int v) {l = max(2ll, l); debug("[%lld %lld] += %lld\n", l, r, v); Seg.add(1, 1, 2 * n, l, r, v); 
//		add(r, v); add(l - 1, -v); }int qry(int x) {int s1 = Bin1.qry(x), s2 = Bin2.qry(x); return (x + 1) * s1 - s2; }int qry(int l, int r) {l = max(2ll, l); 
//		debug("qry[%lld %lld] is %lld\n", l, r, Seg.qry(1, 1, 2 * n, l, r)); return Seg.qry(1, 1, 2 * n, l, r); 
//		return qry(r) - qry(l - 1); }
}Bin;struct Trie_tree {int tot = 0; int w[N], son[N], f[N], top[N], cnt[N]; int dfn[N]; vector<int>G[N]; void add_edge(int x, int y, int c) { G[x].pb(y); f[y] = x; }void dfs1(int x) {w[x] = 1; for(int y : G[x]) {dfs1(y); w[x] += w[y]; if(w[y] > w[son[x]]) son[x] = y; } }void dfs2(int x, int Top) {top[x] = Top; dfn[x] = ++tot; debug("Pou %lld [%lld]\n", x, Top); if(son[x]) dfs2(son[x], Top); for(int y : G[x]) if(y != son[x]) dfs2(y, y); }void work() {dfs1(1); dfs2(1, 1); }void calc_add(int x, int v) {while(x) {int l = dfn[top[x]], r = dfn[x]; if(v == -1) Bin.add(l, r, v); ans += Bin.qry(l, r) * v; if(v == 1) Bin.add(l, r, v); x = f[top[x]]; }}void modify(int x) {calc_add(x, cnt[x] ? -1 : 1); cnt[x] ^= 1; }
}Trie;struct Sam {int i, j, k, tot, lst; int len[N], fail[N], nxt[N][6]; vector<int>G[N]; void init() { tot = lst = 1; }int ins(int x) {int u = ++tot, p, q, cq; p = lst; lst = u; len[u] = len[p] + 1; while(p && !nxt[p][x]) nxt[p][x] = u, p = fail[p]; if(!p) return fail[u] = 1, u; q = nxt[p][x]; if(len[p] + 1 == len[q]) fail[u] = q; else {cq = ++tot; fail[cq] = fail[q]; fail[q] = fail[u] = cq; len[cq] = len[p] + 1; memcpy(nxt[cq], nxt[q], sizeof(nxt[q])); while(p && nxt[p][x] == q) nxt[p][x] = cq, p = fail[p]; }return u; }int dep[N], f[N][22]; void dfs(int x, int fa) {dep[x] = dep[fa] + 1; for(int y : G[x]) dfs(y, x); }void work() {for(i = 1; i <= tot; ++i) {for(j = 1; j <= 4; ++j) {k = nxt[i][j]; if(k) debug("%lld --%c--> %lld\n", i, (char)(j + 'a' - 1), k); if(len[i] + 1 == len[k]) {Trie.add_edge(i, k, j); debug("%lld --> %lld\n", i, k);  }}}for(i = 1; i <= tot; ++i) debug("%lld ", fail[i]); debug("\n"); for(i = 2; i <= tot; ++i) G[fail[i]].pb(i), f[i][0] = fail[i]; for(k = 1; k <= 20; ++k) for(i = 1; i <= tot; ++i) f[i][k] = f[f[i][k - 1]][k - 1]; dfs(1, 0); }int lca(int x, int y) {if(x == y) return x; if(dep[x] < dep[y]) swap(x, y); for(int k = 20; k >= 0; --k)if(dep[f[x][k]] >= dep[y]) x = f[x][k]; if(x == y) return x; for(int k = 20; k >= 0; --k)if(f[x][k] != f[y][k]) x = f[x][k], y = f[y][k]; return f[x][0]; }}SAM;char s[N]; 
int point[N]; signed main()
{#ifdef LOCALfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);#endif
//	srand(time(NULL));
//	T=read();
//	while(T--) {
//
//	}scanf("%s", s + 1); n = strlen(s + 1); SAM.init(); for(i = 1; i <= n; ++i) point[i] = SAM.ins(s[i] - 'a' + 1); SAM.work(); Trie.work(); int Q = read(); while(Q--) {int x, y, z; x = read(); y = read(); z = SAM.lca(point[x], point[y]); debug("max(%lld %lld) = %lld\n", x, y, z); Trie.modify(z); printf("%lld\n", ans); }return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • STL—list—模拟实现【迭代器的实现(重要)】【基本接口的实现】
  • 记录 Ant Design Table 组件使用的问题
  • 深入理解HTTP的基础知识:请求-响应过程解析
  • Google Ads投放 | 如何分析竞争对手的谷歌广告?
  • 【仿真与实物设计】基于51单片机设计的打地鼠游戏机——程序源码原理图proteus仿真图PCB设计文档演示视频元件清单等(文末工程资料下载)
  • 黑神话悟空,高清壁纸、原画,游戏截图
  • EXCEL格式转化
  • ARM 裸机与 Linux 驱动对比及 Linux 内核入门
  • 特斯拉电动卡车事故引发安全调查,汽车制造商电动车战略调整
  • 【网络编程】第八章 传输层-udp(netstat+pidof+upd协议)
  • 继Ollama之后,Go在AI领域再下一城
  • Vue3学习——Node环境安装(一)
  • 【Redis】数据结构和内部编码
  • 模型量化方法-GPTQ
  • python中%s是什么
  • [Vue CLI 3] 配置解析之 css.extract
  • 「译」Node.js Streams 基础
  • 【391天】每日项目总结系列128(2018.03.03)
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • Git 使用集
  • gulp 教程
  • JavaScript对象详解
  • JavaWeb(学习笔记二)
  • Redash本地开发环境搭建
  • Spring框架之我见(三)——IOC、AOP
  • vue.js框架原理浅析
  • 电商搜索引擎的架构设计和性能优化
  • 关于字符编码你应该知道的事情
  • 警报:线上事故之CountDownLatch的威力
  • 排序算法学习笔记
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 线性表及其算法(java实现)
  • 移动端 h5开发相关内容总结(三)
  • 移动端唤起键盘时取消position:fixed定位
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • # 利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
  • #、%和$符号在OGNL表达式中经常出现
  • #define 用法
  • #include
  • #VERDI# 关于如何查看FSM状态机的方法
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • $.ajax中的eval及dataType
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (AngularJS)Angular 控制器之间通信初探
  • (Python第六天)文件处理
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (算法)N皇后问题
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)Oracle存储过程编写经验和优化措施
  • (转载)hibernate缓存
  • (转载)OpenStack Hacker养成指南
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法