返回顶部

树链剖分

暂存的题目集,后面再做整理

先对树跑一遍dfs获得其dfs序,然后对以某个结点为根的子树进行操作,转化为dfs序上对区间的操作,这个过程可以使用线段树完成

欧拉序:跑dfs的时候,将第一次出现该点的时候,记录为1,第二次出现时记录为-1,对树上链的操作就转化成前缀和了,过程可用线段树维护区间和

询问子树的和等操作等价于询问区间\([intime_u,outtime_u]\)的和

习题:

P3178 [HAOI2015]树上操作 :跑一遍dfs记录欧拉序和用线段树维护前缀和

P3833 [SHOI2012]魔法树 : 欧拉序,对于\(u \to v\)这一条链的修改操作,设\(p = LCA(u , v)\), 那么就是树上差分,先对区间\([intime_p , intime_u]\)和区间\([intime_p , intime_v]\)进行区间\(+val\),然后对\(intime_p\)进行单点修改\(-val\)​。

P2590 [ZJOI2008]树的统计 树剖+ 单点修改+ 区间最值 + 区间求和

P3384 【模板】轻重链剖分/树链剖分 :两遍dfs,然后用线段树进行维护即可

const int N = 1e5 + 5;
vector<vector<int>>graph(N);
int  mod;
int a[N];
int fa[N], dep[N], siz[N], son[N];
void dfs1(int u, int f) {
	fa[u] = f; dep[u] = dep[f] + 1;
	siz[u] = 1;
	int maxsize = -1;
	for (auto g : graph[u]) {
		if (g == f) continue;
		dfs1(g, u);
		siz[u] += siz[g];
		if (siz[g] > maxsize) maxsize = siz[g], son[u] = g;
	}
	return;
}
int tim, dfn[N], top[N], weight[N];
void dfs2(int u, int t) {
	dfn[u] = ++tim;
	top[u] = t;
	weight[tim] = a[u];
	if (!son[u]) return;
	dfs2(son[u], t);
	for (auto g : graph[u]) {
		if (g == fa[u] || g == son[u]) continue;
		dfs2(g, g);
	}
	return;
}
struct Tree {
	long long sum, tag;
	int lr, rs, mid;
};
Tree tree[N << 2];
void pushUp(int rt) {
	tree[rt].sum = (tree[rt << 1].sum + tree[rt << 1 | 1].sum) % mod;
	return;
}
void buildTree(int rt, int lr, int rs) {
	tree[rt].lr = lr; tree[rt].rs = rs;
	if (lr == rs) {
		tree[rt].sum = weight[lr];
		return;
	}
	int mid = tree[rt].mid = lr + rs >> 1;
	buildTree(rt << 1, lr, mid);
	buildTree(rt << 1 | 1, mid + 1, rs);
	pushUp(rt);
	return;
}
void pushDown(int rt) {
	if (tree[rt].tag == 0) return;
	long long tag = tree[rt].tag;
	tree[rt].tag = 0;
	tree[rt << 1].sum = (tree[rt << 1].sum + tag * (tree[rt << 1].rs - tree[rt << 1].lr + 1)) % mod;
	tree[rt << 1 | 1].sum = (tree[rt << 1 | 1].sum + tag * (tree[rt << 1 | 1].rs - tree[rt << 1 | 1].lr + 1)) % mod;
	(tree[rt << 1].tag += tag) %= mod;
	(tree[rt << 1 | 1].tag += tag) %= mod;
	return;
}
void update(int rt, int lr, int rs, int val) {
	if (lr > tree[rt].rs || tree[rt].lr > rs) return;
	if (tree[rt].lr >= lr && tree[rt].rs <= rs) {
		(tree[rt].sum += 1ll * val * (tree[rt].rs - tree[rt].lr + 1ll)) %= mod;
		(tree[rt].tag += val) %= mod;
		return;
	}
	pushDown(rt);
	if (tree[rt].mid >= lr) update(rt << 1, lr, rs, val);
	if (tree[rt].mid < rs) update(rt << 1 | 1, lr, rs, val);
	pushUp(rt);
	return;
}
long long query(int rt, int lr, int rs) {
	if (tree[rt].rs < lr || tree[rt].lr > rs) return 0;
	if (tree[rt].lr >= lr && tree[rt].rs <= rs) return tree[rt].sum;
	pushDown(rt);
	long long res = 0;
	if (tree[rt].mid >= lr) res += query(rt << 1, lr, rs);
	if (tree[rt].mid < rs) res += query(rt << 1 | 1, lr, rs);
	res %= mod;
	return res;
}
void updateSon(int x, int val) {
	update(1, dfn[x], dfn[x] + siz[x] - 1, val);
}
int querySon(int x) {
	return query(1, dfn[x], dfn[x] + siz[x] - 1);
}
void updateChain(int u, int v, int val) {
	val %= mod;
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
		update(1, dfn[top[u]], dfn[u], val);
		u = fa[top[u]];
	}
	if (dep[u] > dep[v]) std::swap(u, v);
	update(1, dfn[u] + , dfn[v], val);
	return;
}
int queryChain(int u, int v) {
	long long res = 0;
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
		res += query(1, dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	}
	if (dep[u] > dep[v]) std::swap(u, v);
	res += query(1, dfn[u], dfn[v]);
	return res % mod;
}

P3038 [USACO11DEC]Grass Planting G LCA + 树链剖分 + 线段树 注意更新的时候要减去LCA的值

CF343D Water Tree :套路题,根据要求操作即可

CF165D Beard Graph :将边权转化成点权,然后模拟

P4315 月下“毛景树” :先转化成点权,查询的时候把LCA存下来,然后修改链之后,再把LCA改回去,询问修改都按照这样的思路

UVA1674 闪电的能量 Lightning Energy Report :T到炸裂,多组注意清空

P3950 部落冲突 :边权设置为1,维护边权即可

P4114 Qtree1 SP375 QTREE - Query on a tree SP375 QTREE - Query on a tree原题链接 :注意清空LCA数组等

CF396C On Changing Tree : \(x - (dep_v - dep_u) * k = (-dep_v *k )+ (x + dep_u * k)\)​​ 第一项的操作等价于\(-dep_v * \sum k\)​​ 拿两个树状数组维护一下即可​​​

SP3978 DISQUERY - Distance Query 树链剖分+线段树维护区间极值

CF383C Propagating tree 用两个树状数组维护深度为奇数和偶数的点即可(dfs + BIT / 线段树)

  • P2486 [SDOI2011]染色 树链剖分加线段树,只是询问的时候合并区间注意一下即可

  • CF916E Jamie and Tree dfs序+ 线段树 + LCA ,由于换根,所以需要对根进行讨论,讨论方法见题解区

  • P3979 遥远的国度 树剖 + 线段树 询问的时候讨论一下根跟当前询问子根的关系即可,具体看题解 \(QAQ\)

posted @ 2021-12-09 21:03  cherish-lgb  阅读(27)  评论(0编辑  收藏  举报