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:

cpp 复制代码
#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 LOCAL
	  freopen("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;
}
相关推荐
XuYueming15 天前
[NOIP2022] 比赛 随机排列 部分分
数学·线段树·题解·单调栈·洛谷·扫描线·二维数点·部分分·概率 & 期望
烧技湾1 个月前
SAM应用:医学图像和视频中的任何内容分割中的基准测试与部署
sam·医学图像分割·分割一切·med2san
summ1ts1 个月前
NOIP2023题解
数据结构·c++·算法·线段树·动态规划·图论·noip2023
MingJunYi1 个月前
P2056 [ZJOI2007] 捉迷藏 题解
开发语言·c++·算法·树剖·线段树分治
ganjiee00072 个月前
leetcode|刷算法 线段树原理以及模板
算法·leetcode·线段树
小夏refresh2 个月前
论文阅读笔记: Segment Anything
论文阅读·笔记·计算机视觉·sam·语义分割
闻缺陷则喜何志丹3 个月前
【线段树】2569. 更新数组后处理求和查询
c++·算法·线段树·力扣·求和·数组·查询
知来者逆3 个月前
SAM 2——视频和图像实时实例分割的全新开源模型
计算机视觉·图像分割·sam·语义分割·实例分割·sam 2·万物分割
沃恩智慧3 个月前
实现分割自动化!基于SAM的医学图像分割又双叒叕有新突破
人工智能·深度学习·自动化·sam·医学图像分割