树链剖分之重链剖分 详解 题目整理

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qcwlmqy/article/details/97887463

树链剖分

题目中出现的树链剖分一般分为两种,重链剖分和长链剖分

  • 重链剖分:选择子树最大的儿子, 将其归入当前点所在 的同一条重链
  • 长链剖分:选择向下能达到的深 度最深的儿子,将其归 入当前点所在的同一 条长链

重剖主要用于维护子树信息和链信息长剖主要用于维护子树中只与深度有关的信息

  • 树链查询:树上前缀 +LCA
  • 树链修改:树上差分
  • 树链修改 & 树链查询:重链剖分 + 树状数组 | 线段树 |……

重剖

基本概念

  • 重子节点:所有子节点中子树大小最大的儿子节点
  • 轻子节点:除重儿子之外的所有子节点
  • 重边:重子节点和其父节点之间连的边
  • 轻边:轻子节点和其父节点之间连的边
  • 重链:重边连接而成的链
  • d f s dfs 序:对树进行深度优先搜索,第一次访问到某个点时对其标号,按标号从小到大排序得到的序列叫 d f s dfs

    子树的 d f s dfs 序是连续的
    树上的子树修改和子树查询,等价于 d f s dfs 序上的区间修改和区间查询

重剖

  • 从根开始对树进行深度优先搜索,同时优先搜索重儿子,在重链的每个点上记录每条重链的起点
  • 优先搜索重儿子操作使得每条重链在dfs序上是连续的

树划分为轻边和重链

重链上的点dfs序连续,可以数据结构维护

轻边的两端点一定分属某条重链上的点

计算重儿子

  1. 标记每个点的深度 d e e p [ ] deep[]
  2. 标记每个点的父亲 f a [ ] fa[]
  3. 标记每个非叶子节点的子树大小(含它自己)
  4. 标记每个非叶子节点的重儿子编号 s o n [ ] son[]
int deep[maxn], n_size[maxn], fa[maxn], son[maxn];			
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

标记重链端点

  1. 标记每个点的新编号
  2. 赋值每个点的初始值到新编号上(若点有权值)
  3. 处理每个点所在链的顶端
  4. 处理每条链
int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
//int wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点 
	id[now] = ++dfn;					//标记每个点的新编号 
	//wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来 
	top[now] = top_node;				//这个点所在链的顶端 
	if (!son[now]) return;				//如果没有儿子则返回 
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理 
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链 
	}
}

LCA

求x,y的最近公共祖先

深度大的点跳到上一条链
直到两点跳到同一条链为止

int lca(int x, int y) {						//x,y为树上两点编号
	for (; top[x] != top[y]; x = fa[top[x]])//若不在一个链上,跳转这条链	
		if (deep[top[x]] < deep[top[y]])	//先跳转深度深的点
			swap(x, y);				
	return deep[x] < deep[y] ? x : y;		//在同一条链上返回深度深的点
}

求x到y的第一个点(x为y的祖先)

重复y找祖先的过程,记录上个链的头
若祖先为一个链的尾节点,返回上个链的头
否则返回重儿子即可

int lca2(int x, int y) {					//计算y的祖先x向y的第一个点
	int t;									//记录上个链的顶点
	while (top[x] != top[y])				//y向上个链跳转
		t = top[y], y = fa[top[y]];
	return x == y ? t : son[x];				//x==y则返回上个链的头,反之返回重儿子即可
}

链操作

重复寻找LCA的过程,处理每个链即可

void chain(int x, int y, int val) {					//x,y为树上两点编号,val操作
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		op(id[top[x]], id[x], val);					//调用线段树操作
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	op(id[y], id[x], val);							//调用线段树操作
}

边权转点权版

void chain(int x, int y, int val) {					//x,y为树上两点编号,val操作
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		op(id[top[x]], id[x], val);					//调用线段树操作
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	op(id[y]+1, id[x], val);						//调用线段树操作,若x==y,线段树不会出问题,不然要调用lca2,抛去lca
}

线段树(友情提供用于区间维护)

LL tree[maxn << 2], lazy[maxn << 2];
void build(int root, int left, int right) {	//建树
	if (left == right) {
		tree[root] = wt[left];
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = (tree[root << 1]+ tree[root << 1 | 1]) % mod;
}
void pushdown(int root, int left, int right) {	//懒标记
	if (!lazy[root]) return;
	int mid = (left + right) >> 1;
	tree[root << 1] = (tree[root << 1] + (LL(mid) - left + 1) * lazy[root] % mod) % mod;
	lazy[root << 1] = (lazy[root << 1] + lazy[root]) % mod;
	tree[root << 1 | 1] = (tree[root << 1 | 1] + (LL(right) - mid) * lazy[root] % mod) % mod;
	lazy[root << 1 | 1] = (lazy[root << 1 | 1] + lazy[root]) % mod;
	lazy[root] = 0;
}
void update(int root, int left, int right, int stdl, int stdr, LL val) {	//区间加
	if (stdl <= left && right <= stdr) {
		tree[root] = (tree[root] + (LL(right) - left + 1) * val % mod);
		lazy[root] = (lazy[root] + val) % mod;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr, val);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr, val);
	tree[root] = (tree[root << 1] + tree[root << 1 | 1]) % mod;
}
LL query(int root, int left, int right, int stdl, int stdr) {	//区间求和
	LL ans = 0;
	if (stdl <= left && right <= stdr) 
		return tree[root];
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = (ans + query(root << 1, left, mid, stdl, stdr)) % mod;
	if (stdr > mid) ans = (ans + query(root << 1 | 1, mid + 1, right, stdl, stdr)) % mod;
	return ans;
}

常见询问

例题 洛谷 P3384 【模板】树链剖分

例题 BZOJ 树的统计Count

BZOJ 4196: [Noi2015]软件包管理器

代码附于文末

1、 l c a lca

调用lca函数即可

2、 x x y y 的链修改,询问

调用上方链操作即可

void chainadd(int x, int y, int val) {					//x,y为树上两点编号,val操作
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		update(1, 1, n, id[top[x]], id[x], val);	//区间加
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	update(1, 1, n, id[y], id[x], val);				//区间加
}

LL chainsum(int x, int y) {					//x,y为树上两点编号,val操作
	LL res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		res = (res + query(1, 1, n, id[top[x]], id[x])) % mod;	//区间求和
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	res = (res + query(1, 1, n, id[y], id[x])) % mod;	//区间求和
	return res;
}

3、以 x x 为根的子树修改和查询

子树的 d f s dfs 序是连续的
树上的子树修改和子树查询,等价于 d f s dfs 序上的区间修改和区间查询

update(1, 1, n, id[x], id[x] + n_size[x] - 1, z);//子树修改

query(1, 1, n, id[x], id[x] + n_size[x] - 1); //子树求和

例题

边权转点权

BZOJ1103 大都市meg

题意:
两个操作,1、将某条边由1变成0,2、将1到x求和
思路:

边权转点权
由点权表示该点上一条边的边权
根无点权

修改:将深度大的点变为0
查询:对1到x求和

直接树链剖分+树状数组即可

代码附于文末

BZOJ2157: 旅游

同上题
有几个注意点
1、将根的min设为inf,max设为-inf而不是0
2、求链的操作时,lca的权值不能加进去
代码附于文末

POJ2763 Housewife Wind

题意:
两个操作: 1 x 1 x 求s到x的路径和并将s变成x 2 x w 2 x w 将x号边变成w
思路:
边权转点权的裸题
代码附于文末

换根操作

BZOJ3083 遥远的国度

题意:
3个操作: 1 x 1 x 将根换成x, 2 x y v 2 x y v x y x-y 变成v, 3 x 3 x 求x的子树的最小值
思路:
换根
如果当前数据指定的根在查询的子树内
则查询操作变为查询根所在分支的子树外的点 (调用 lca2)
可以由两个区间( 1 i d [ x ] 1 1-id[x]-1 i d [ x ] + s i z e [ x ] n id[x]+size[x]-n )最小值合并得到

如果根在子树外,直接查询子树即可
代码附于文末

链合并有方向性

HDU5893 List wants to travel

题意:
2个操作: C h a n g e x y z Change x y z 将x到y的链变成z, Q u e r y x y Query x y 求x到y的链上的不同的连续数的个数,比如 11221 11221 有三个
思路:
边权转点权
注意剖去lca
链合并带有方向性

int chain_query(int x, int y) {
	int prex = -1, prey = -1, res = 0;		//记录两个点的前端点
	Tree T;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]]) {
			swap(x, y);
			swap(prex, prey);
		}
		T = query(1, 1, n, id[top[x]], id[x]);
		res += T.data;
		if (T.suf == prex)res--;
		prex = T.pre;
	}
	if (deep[x] < deep[y]) {
		swap(x, y);
		swap(prex, prey);
	}
	if (x == y) {		//特判lca为重链最后一个点时
		if (prex == prey)res--;
		return res;
	}
	T = query(1, 1, n, id[y] + 1, id[x]);
	res += T.data;
	if (prex == T.suf)res--;
	if (prey == T.pre)res--;
	return res;
}

完整模板

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 100005;
const int maxm = 100005;
const LL mod = 1e9;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];
void dfs1(int now, int f, int depth) {
	deep[now] = depth;
	fa[now] = f;
	n_size[now] = 1;
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;
		dfs1(edge[i].v, now, depth + 1);
		n_size[now] += n_size[edge[i].v];
		if (n_size[edge[i].v] > n_size[son[now]])
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;
int wt[maxn], w[maxn];
void dfs2(int now, int top_node) {
	id[now] = ++dfn;
	wt[dfn] = w[now];
	top[now] = top_node;
	if (!son[now]) return;
	dfs2(son[now], top_node);
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);
	}
}

int lca(int x, int y) {
	for (; top[x] != top[y]; x = fa[top[x]])
		if (deep[top[x]] < deep[top[y]])
			swap(x, y);
	return deep[x] < deep[y] ? x : y;
}

int lca2(int x, int y) {
	int t;
	while (top[x] != top[y])
		t = top[y], y = fa[top[y]];
	return x == y ? t : son[x];
}

LL tree[maxn << 2], lazy[maxn << 2];
void build(int root, int left, int right) {
	if (left == right) {
		tree[root] = wt[left];
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
void pushdown(int root, int left, int right) {
	if (!lazy[root]) return;
	int mid = (left + right) >> 1;
	tree[root << 1] = tree[root << 1] + (LL(mid) - left + 1) * lazy[root];
	lazy[root << 1] = lazy[root << 1] + lazy[root];
	tree[root << 1 | 1] = tree[root << 1 | 1] + (LL(right) - mid) * lazy[root];
	lazy[root << 1 | 1] = lazy[root << 1 | 1] + lazy[root];
	lazy[root] = 0;
}
void update(int root, int left, int right, int stdl, int stdr, LL val) {
	if (stdl <= left && right <= stdr) {
		tree[root] = tree[root] + (LL(right) - left + 1) * val;
		lazy[root] = lazy[root] + val;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr, val);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr, val);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
LL query(int root, int left, int right, int stdl, int stdr) {
	LL ans = 0;
	if (stdl <= left && right <= stdr)
		return tree[root];
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = ans + query(root << 1, left, mid, stdl, stdr);
	if (stdr > mid) ans = ans + query(root << 1 | 1, mid + 1, right, stdl, stdr);
	return ans;
}

void chain(int x, int y, int val) {
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		op(id[top[x]], id[x], val);
	}
	if (deep[x] < deep[y])swap(x, y);
	op(id[y], id[x], val);
}

例题代码

洛谷 P3384 【模板】树链剖分

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 100005;
const int maxm = 100005;
int n, m, s;
LL mod;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
LL wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点
	id[now] = ++dfn;					//标记每个点的新编号
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来
	top[now] = top_node;				//这个点所在链的顶端
	if (!son[now]) return;				//如果没有儿子则返回
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链
	}
}

LL tree[maxn << 2], lazy[maxn << 2];
void build(int root, int left, int right) {
	if (left == right) {
		tree[root] = wt[left];
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = (tree[root << 1]+ tree[root << 1 | 1]) % mod;
}
void pushdown(int root, int left, int right) {
	if (!lazy[root]) return;
	int mid = (left + right) >> 1;
	tree[root << 1] = (tree[root << 1] + (LL(mid) - left + 1) * lazy[root] % mod) % mod;
	lazy[root << 1] = (lazy[root << 1] + lazy[root]) % mod;
	tree[root << 1 | 1] = (tree[root << 1 | 1] + (LL(right) - mid) * lazy[root] % mod) % mod;
	lazy[root << 1 | 1] = (lazy[root << 1 | 1] + lazy[root]) % mod;
	lazy[root] = 0;
}
void update(int root, int left, int right, int stdl, int stdr, LL val) {
	if (stdl <= left && right <= stdr) {
		tree[root] = (tree[root] + (LL(right) - left + 1) * val % mod);
		lazy[root] = (lazy[root] + val) % mod;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr, val);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr, val);
	tree[root] = (tree[root << 1] + tree[root << 1 | 1]) % mod;
}
LL query(int root, int left, int right, int stdl, int stdr) {
	LL ans = 0;
	if (stdl <= left && right <= stdr) 
		return tree[root];
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = (ans + query(root << 1, left, mid, stdl, stdr)) % mod;
	if (stdr > mid) ans = (ans + query(root << 1 | 1, mid + 1, right, stdl, stdr)) % mod;
	return ans;
}

void chain1(int x, int y, int val) {					//x,y为树上两点编号,val操作
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		update(1, 1, n, id[top[x]], id[x], val);	//调用线段树操作
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	update(1, 1, n, id[y], id[x], val);				//调用线段树操作
}

LL chain2(int x, int y) {					//x,y为树上两点编号,val操作
	LL res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		res = (res + query(1, 1, n, id[top[x]], id[x])) % mod;	//调用线段树操作
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	res = (res + query(1, 1, n, id[y], id[x])) % mod;	//调用线段树操作
	return res;
}

int main() {
	scanf("%d%d%d%lld", &n, &m, &s, &mod);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &w[i]);
	int u, v;
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &u, &v);
		AddEdge(u, v);
		AddEdge(v, u);
	}
	dfs1(s, 0, 1);
	dfs2(s, s);
	build(1, 1, n);
	int g, x, y; LL z;
	while (m--) {
		scanf("%d", &g);
		if (g == 1) {
			scanf("%d%d%lld", &x, &y, &z);
			z %= mod;
			chain1(x, y, z);
		}
		else if (g == 2) {
			scanf("%d%d", &x, &y);
			printf("%lld\n", chain2(x, y));
		}
		else if (g == 3) {
			scanf("%d%lld", &x, &z);
			z %= mod;
			update(1, 1, n, id[x], id[x] + n_size[x] - 1, z);
		}
		else {
			scanf("%d", &x);
			printf("%lld\n", query(1, 1, n, id[x], id[x] + n_size[x] - 1));
		}
	}
}

树的统计Count

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
typedef pair<int, int> pii;
const int maxn = 30005;
const int maxm = 30005;
const int inf = 0X3f3f3f3f;
const int mod = 1e9+7;
int n, q;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];			
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
int wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点 
	id[now] = ++dfn;					//标记每个点的新编号 
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来 
	top[now] = top_node;				//这个点所在链的顶端 
	if (!son[now]) return;				//如果没有儿子则返回 
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理 
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链 
	}
}

pii tree[maxn << 2];
void build(int root, int left, int right) {
	if (left == right) {
		tree[root].first = wt[left];
		tree[root].second = wt[left];
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root].first = tree[root << 1].first + tree[root << 1 | 1].first;
	tree[root].second = max(tree[root << 1].second, tree[root << 1 | 1].second);
}
void update(int root, int left, int right, int x, int val) {
	if (left == right) {
		tree[root].first = tree[root].second = val;
		return;
	}
	int mid = (left + right) >> 1;
	if (x <= mid) update(root << 1, left, mid, x, val);
	else update(root << 1 | 1, mid + 1, right, x, val);
	tree[root].first = (tree[root << 1].first + tree[root << 1 | 1].first);
	tree[root].second = max(tree[root << 1].second, tree[root << 1 | 1].second);
}
int query_first(int root, int left, int right, int stdl, int stdr) {
	int ans = 0;
	if (stdl <= left && right <= stdr)
		return tree[root].first;
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = (ans + query_first(root << 1, left, mid, stdl, stdr));
	if (stdr > mid) ans = (ans + query_first(root << 1 | 1, mid + 1, right, stdl, stdr));
	return ans;
}
int query_second(int root, int left, int right, int stdl, int stdr) {
	int ans = -inf;
	if (stdl <= left && right <= stdr)
		return tree[root].second;
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = max(ans, query_second(root << 1, left, mid, stdl, stdr));
	if (stdr > mid) ans = max(ans, query_second(root << 1 | 1, mid + 1, right, stdl, stdr));
	return ans;
}
int chain_first(int x, int y) {					//x,y为树上两点编号,val操作
	int res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		res += query_first(1, 1, n, id[top[x]], id[x]);					//调用线段树操作
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	res += query_first(1, 1, n, id[y], id[x]);							//调用线段树操作
	return res;
}
int chain_second(int x, int y) {					//x,y为树上两点编号,val操作
	int res = -inf;
	for (; top[x] != top[y]; x = fa[top[x]]) {		//若不在一个链上,计算这条链
		if (deep[top[x]] < deep[top[y]])swap(x, y);	//先计算深度深的点
		res = max(res, query_second(1, 1, n, id[top[x]], id[x]));					//调用线段树操作
	}
	if (deep[x] < deep[y])swap(x, y);				//在一条链上
	res = max(res, query_second(1, 1, n, id[y], id[x]));							//调用线段树操作
	return res;
}

int main() {
	scanf("%d", &n);
	int u, v;
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &u, &v);
		AddEdge(u, v);
		AddEdge(v, u);
	}
	for (int i = 1; i <= n; i++)
		scanf("%d", &w[i]);
	dfs1(1, 0, 1);
	dfs2(1, 1);
	build(1, 1, n);
	char s[10];
	int x;
	int m; scanf("%d", &m);
	while (m--) {
		scanf("%s", s);
		if (s[0] == 'C') {
			scanf("%d%d", &x, &v);
			update(1, 1, n, id[x], v);
		}
		else if (s[1] == 'S') {
			scanf("%d%d", &u, &v);
			printf("%d\n", chain_first(u, v));
		}
		else {
			scanf("%d%d", &u, &v);
			printf("%d\n", chain_second(u, v));
		}
	}
}

[Noi2015]软件包管理器

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 200005;
const int maxm = 200005;
int n;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];			
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
int wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点 
	id[now] = ++dfn;					//标记每个点的新编号 
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来 
	top[now] = top_node;				//这个点所在链的顶端 
	if (!son[now]) return;				//如果没有儿子则返回 
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理 
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链 
	}
}

int tree[maxn << 2], lazy[maxn << 2];
void build(int root, int left, int right) {
	if (left == right) {
		tree[root] = 0;
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
void pushdown(int root, int left, int right) {
	if (lazy[root] == -1) return;
	int mid = (left + right) >> 1;
	tree[root << 1] = (mid - left + 1) * lazy[root];
	tree[root << 1 | 1] = (right - mid) * lazy[root];
	lazy[root << 1] = lazy[root << 1 | 1] = lazy[root];
	lazy[root] = -1;
}
void update(int root, int left, int right, int stdl, int stdr, int val) {
	if (stdl <= left && right <= stdr) {
		tree[root] = (right - left + 1) * val;
		lazy[root] = val;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr, val);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr, val);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
int query(int root, int left, int right, int stdl, int stdr) {
	int ans = 0;
	if (stdl <= left && right <= stdr)
		return tree[root];
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = (ans + query(root << 1, left, mid, stdl, stdr));
	if (stdr > mid) ans = (ans + query(root << 1 | 1, mid + 1, right, stdl, stdr)) ;
	return ans;
}

void chain_update(int x, int y, int val) {					
	for (; top[x] != top[y]; x = fa[top[x]]) {	
		if (deep[top[x]] < deep[top[y]])swap(x, y);	
		update(1, 1, n, id[top[x]], id[x], val);
	}
	if (deep[x] < deep[y])swap(x, y);		
	update(1, 1, n, id[y], id[x], val);
}
int chain_query(int x, int y) {
	int res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		res += query(1, 1, n, id[top[x]], id[x]);
	}
	if (deep[x] < deep[y])swap(x, y);
	res += query(1, 1, n, id[y], id[x]);
	return res;
}

int main() {
	memset(lazy, -1, sizeof(lazy));
	scanf("%d", &n);
	int u, v;
	for (int i = 2; i <= n; i++) {
		scanf("%d", &v);
		AddEdge(i, v + 1);
		AddEdge(v + 1, i);
	}
	dfs1(1, 0, 1);
	dfs2(1, 1);
	build(1, 1, n);
	int q; scanf("%d", &q);
	char s[11]; int x, sum = 0, cnt;
	while (q--) {
		scanf("%s", s);
		scanf("%d", &x); x++;
		if (s[0] == 'i') {
			chain_update(1, x, 1);
			cnt = query(1, 1, n, 1, n);
			printf("%d\n", cnt - sum);
			sum = cnt;
		}
		else {
			update(1, 1, n, id[x], id[x] + n_size[x] - 1, 0);
			cnt = query(1, 1, n, 1, n);
			printf("%d\n", sum - cnt);
			sum = cnt;
		}
	}
}

大都市meg

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 250005;
const int maxm = 250005;
const LL mod = 1e9;
const int inf = 0X3f3f3f3f;
int n;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];			
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
int wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点 
	id[now] = ++dfn;					//标记每个点的新编号 
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来 
	top[now] = top_node;				//这个点所在链的顶端 
	if (!son[now]) return;				//如果没有儿子则返回 
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理 
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链 
	}
}

int tree[maxn];
inline int lowbit(int x) {
	return x & -x;
}
void update(int x, int v) {
	while (x <= n) {
		tree[x] += v;
		x += lowbit(x);
	}
}
int query(int x) {
	int res = 0;
	while (x) {
		res += tree[x];
		x -= lowbit(x);
	}
	return res;
}

int chain(int x, int y) {
	int res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		res += query(id[x]) - query(id[top[x]] - 1);
	}
	if (deep[x] < deep[y])swap(x, y);
	res += query(id[x]) - query(id[y] - 1);
	return res;
}
int main() {
	scanf("%d", &n);
	w[1] = 0;
	for (int i = 2; i <= n; i++)w[i] = 1;
	int u, v;
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &u, &v);
		AddEdge(u, v);
		AddEdge(v, u);
	}
	dfs1(1, 0, 1);
	dfs2(1, 1);
	for (int i = 1; i <= n; i++) 
		update(id[i], wt[i]);
	int m; scanf("%d", &m);
	m += n - 1;
	char s[10];
	while (m--) {
		scanf("%s", s);
		if (s[0] == 'W') {
			scanf("%d", &v);
			printf("%d\n", chain(1, v));
		}
		else {
			scanf("%d%d", &u, &v);
			if (deep[u] > deep[v])swap(u, v);
			update(id[v], -1);
		}
	}
}

2157: 旅游

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 20005;
const int maxm = 20005;
const int inf = 0X3f3f3f3f;
int n;

int head[maxn], tot;
struct Edge {
	int v;
	int next;
} edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
int wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点
	id[now] = ++dfn;					//标记每个点的新编号
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来
	top[now] = top_node;				//这个点所在链的顶端
	if (!son[now]) return;				//如果没有儿子则返回
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链
	}
}

struct Tree {
	int data;
	int max;
	int min;
} tree[maxn << 2];
int lazy[maxn << 2];
void build(int root, int left, int right) {
	lazy[root] = 1;
	if (left == right) {
		tree[root].data = tree[root].max = tree[root].min = wt[left];
		if (left == 1)tree[root].max = -inf, tree[root].min = inf;
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root].data = tree[root << 1].data + tree[root << 1 | 1].data;
	tree[root].max = max(tree[root << 1].max, tree[root << 1 | 1].max);
	tree[root].min = min(tree[root << 1].min, tree[root << 1 | 1].min);
}
void pushdown(int root, int left, int right) {
	if (lazy[root] == 1) return;
	int mid = (left + right) >> 1;
	swap(tree[root << 1].max, tree[root << 1].min);
	swap(tree[root << 1 | 1].max, tree[root << 1 | 1].min);
	tree[root << 1].data = -tree[root << 1].data;
	tree[root << 1 | 1].data = -tree[root << 1 | 1].data;
	tree[root << 1].min = -tree[root << 1].min;
	tree[root << 1 | 1].min = -tree[root << 1 | 1].min;
	tree[root << 1].max = -tree[root << 1].max;
	tree[root << 1 | 1].max = -tree[root << 1 | 1].max;
	lazy[root << 1] = -lazy[root << 1];
	lazy[root << 1 | 1] = -lazy[root << 1 | 1];
	lazy[root] = 1;
}
void update(int root, int left, int right, int stdl, int stdr) {
	if (stdl <= left && right <= stdr) {
		swap(tree[root].max, tree[root].min);
		tree[root].data = -tree[root].data;
		tree[root].max = -tree[root].max;
		tree[root].min = -tree[root].min;
		lazy[root] = -lazy[root];
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr);
	tree[root].data = tree[root << 1].data + tree[root << 1 | 1].data;
	tree[root].max = max(tree[root << 1].max, tree[root << 1 | 1].max);
	tree[root].min = min(tree[root << 1].min, tree[root << 1 | 1].min);
}
void modify(int root, int left, int right, int x, int val) {
	if (left == right) {
		tree[root].data = tree[root].max = tree[root].min = val;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (x <= mid) modify(root << 1, left, mid, x, val);
	else modify(root << 1 | 1, mid + 1, right, x, val);
	tree[root].data = tree[root << 1].data + tree[root << 1 | 1].data;
	tree[root].max = max(tree[root << 1].max, tree[root << 1 | 1].max);
	tree[root].min = min(tree[root << 1].min, tree[root << 1 | 1].min);
}
int query_sum(int root, int left, int right, int stdl, int stdr) {
	int ans = 0;
	if (stdl <= left && right <= stdr)
		return tree[root].data;
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = ans + query_sum(root << 1, left, mid, stdl, stdr);
	if (stdr > mid) ans = ans + query_sum(root << 1 | 1, mid + 1, right, stdl, stdr);
	return ans;
}
int query_max(int root, int left, int right, int stdl, int stdr) {
	if (stdl <= left && right <= stdr)
		return tree[root].max;
	pushdown(root, left, right);
	int mid = (left + right) >> 1, ans = -inf;
	if (stdl <= mid) ans = max(ans, query_max(root << 1, left, mid, stdl, stdr));
	if (stdr > mid) ans = max(ans, query_max(root << 1 | 1, mid + 1, right, stdl, stdr));
	return ans;
}
int query_min(int root, int left, int right, int stdl, int stdr) {
	if (stdl <= left && right <= stdr)
		return tree[root].min;
	pushdown(root, left, right);
	int mid = (left + right) >> 1, ans = inf;
	if (stdl <= mid) ans = min(ans, query_min(root << 1, left, mid, stdl, stdr));
	if (stdr > mid) ans = min(ans, query_min(root << 1 | 1, mid + 1, right, stdl, stdr));
	return ans;
}
void chain_update(int x, int y) {
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		update(1, 1, n, id[top[x]], id[x]);
	}
	if (deep[x] < deep[y])swap(x, y);
	update(1, 1, n, id[y] + 1, id[x]);
}
int chain_sum(int x, int y) {
	int res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		res += query_sum(1, 1, n, id[top[x]], id[x]);
	}
	if (deep[x] < deep[y])swap(x, y);
	res += query_sum(1, 1, n, id[y] + 1, id[x]);
	return res;
}
int chain_max(int x, int y) {
	int res = -inf;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		res = max(res, query_max(1, 1, n, id[top[x]], id[x]));
	}
	if (deep[x] < deep[y])swap(x, y);
	res = max(res, query_max(1, 1, n, id[y] + 1, id[x]));
	return res;
}
int chain_min(int x, int y) {
	int res = inf;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		res = min(res, query_min(1, 1, n, id[top[x]], id[x]));
	}
	if (deep[x] < deep[y])swap(x, y);
	res = min(res, query_min(1, 1, n, id[y] + 1, id[x]));
	return res;
}
int u[maxn], v[maxn], s[maxn];
int main() {
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d%d", &u[i], &v[i], &s[i]);
		u[i]++;
		v[i]++;
		AddEdge(u[i], v[i]);
		AddEdge(v[i], u[i]);
	}
	dfs1(1, 0, 1);
	for (int i = 1; i < n; i++) {
		if (deep[u[i]] > deep[v[i]]) swap(u[i], v[i]);
		w[v[i]] = s[i];
	}
	dfs2(1, 1);
	build(1, 1, n);
	for (int i = 1; i <= 3; i++)
		cout << wt[i] << ' ';
	cout << '\n';
	cout << query_max(1, 1, n, 2, 1);
	int m;
	scanf("%d", &m);
	char s[10];
	int x, y, z;
	while (m--) {
		scanf("%s", s);
		if (s[0] == 'C') {
			scanf("%d%d", &x, &z);
			modify(1, 1, n, id[v[x]], z);
		}
		else if (s[0] == 'N') {
			scanf("%d%d", &x, &y);
			x++;
			y++;
			chain_update(x, y);
		}
		else if (s[0] == 'S') {
			scanf("%d%d", &x, &y);
			x++;
			y++;
			printf("%d\n", chain_sum(x, y));
		}
		else if (s[1] == 'A') {
			scanf("%d%d", &x, &y);
			x++;
			y++;
			printf("%d\n", chain_max(x, y));
		}
		else {
			scanf("%d%d", &x, &y);
			x++;
			y++;
			printf("%d\n", chain_min(x, y));
		}
	}
}

Housewife Wind

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 100005;
const int maxm = 100005;
const LL mod = 1e9;
int n, q, s;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];			
void dfs1(int now, int f, int depth) {	
	deep[now] = depth;						
	fa[now] = f;							
	n_size[now] = 1;							
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;		
		dfs1(edge[i].v, now, depth + 1);	
		n_size[now] += n_size[edge[i].v];	
		if (n_size[edge[i].v] > n_size[son[now]])
			son[now] = edge[i].v;
	}
}
int top[maxn], id[maxn], dfn;			
int wt[maxn], w[maxn];			
void dfs2(int now, int top_node) {		
	id[now] = ++dfn;			
	wt[dfn] = w[now];				
	top[now] = top_node;		
	if (!son[now]) return;			
	dfs2(son[now], top_node);		
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);	
	}
}

int tree[maxn << 2], lazy[maxn << 2];
void build(int root, int left, int right) {
	if (left == right) {
		tree[root] = wt[left];
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
void update(int root, int left, int right, int x, int val) {
	if (left == right) {
		tree[root] = val;
		return;
	}
	int mid = (left + right) >> 1;
	if (x <= mid) update(root << 1, left, mid, x, val);
	else update(root << 1 | 1, mid + 1, right, x, val);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
int query(int root, int left, int right, int stdl, int stdr) {
	int ans = 0;
	if (stdl <= left && right <= stdr)
		return tree[root];
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = ans + query(root << 1, left, mid, stdl, stdr);
	if (stdr > mid) ans = ans + query(root << 1 | 1, mid + 1, right, stdl, stdr);
	return ans;
}

int chain_query(int x, int y) {	
	int res = 0;
	for (; top[x] != top[y]; x = fa[top[x]]) {		
		if (deep[top[x]] < deep[top[y]])swap(x, y);	
		res += query(1, 1, n, id[top[x]], id[x]);
	}
	if (deep[x] < deep[y])swap(x, y);			
	res += query(1, 1, n, id[y] + 1, id[x]);
	return res;
}

int u[maxn], v[maxn], p[maxn];
int main() {
	scanf("%d%d%d", &n, &q, &s);
	for (int i = 1; i < n; i++) {
		scanf("%d%d%d", &u[i], &v[i], &p[i]);
		AddEdge(u[i], v[i]);
		AddEdge(v[i], u[i]);
	}
	dfs1(1, 0, 1);
	for (int i = 1; i < n; i++) {
		if (deep[u[i]] > deep[v[i]]) swap(u[i], v[i]);
		w[v[i]] = p[i];
	}
	dfs2(1, 1);
	build(1, 1, n);
	int opt, x, y, z;
	while (q--) {
		scanf("%d", &opt);
		if (opt == 0) {
			scanf("%d", &x);
			printf("%d\n", chain_query(s, x));
			s = x;
		}
		else {
			scanf("%d%d", &x, &z);
			update(1, 1, n, id[v[x]], z);
		}
	}
}

遥远的国度

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 100005;
const int maxm = 100005;
const LL inf = 1e18;
int n, m;

int head[maxn], tot;
struct Edge
{
	int v;
	int next;
}edge[maxm << 1];
void init() {
	memset(head, 0, sizeof(head));
	tot = 0;
}
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}

int deep[maxn], n_size[maxn], fa[maxn], son[maxn];			
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}

int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
LL wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点 
	id[now] = ++dfn;					//标记每个点的新编号 
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来 
	top[now] = top_node;				//这个点所在链的顶端 
	if (!son[now]) return;				//如果没有儿子则返回 
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理 
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链 
	}
}

int lca2(int x, int y) {					//计算y的祖先x向y的第一个点
	int t;									//记录上个链的顶点
	while (top[x] != top[y])				//y向上个链跳转
		t = top[y], y = fa[top[y]];
	return x == y ? t : son[x];				//x==y则返回上个链的头,反之返回重儿子即可
}

LL tree[maxn << 2], lazy[maxn << 2];
void build(int root, int left, int right) {
	if (left == right) {
		tree[root] = wt[left];
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = min(tree[root << 1], tree[root << 1 | 1]);
}
void pushdown(int root, int left, int right) {
	if (lazy[root] == -1) return;
	int mid = (left + right) >> 1;
	tree[root << 1] = tree[root << 1 | 1] = lazy[root];
	lazy[root << 1] = lazy[root << 1 | 1] = lazy[root];
	lazy[root] = -1;
}
void update(int root, int left, int right, int stdl, int stdr, LL val) {
	if (stdl <= left && right <= stdr) {
		tree[root] = val;
		lazy[root] = val;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr, val);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr, val);
	tree[root] = min(tree[root << 1], tree[root << 1 | 1]);
}
LL query(int root, int left, int right, int stdl, int stdr) {
	if (stdl <= left && right <= stdr)
		return tree[root];
	pushdown(root, left, right);
	LL ans = inf;
	int mid = (left + right) >> 1;
	if (stdl <= mid) ans = min(ans, query(root << 1, left, mid, stdl, stdr));
	if (stdr > mid) ans = min(ans, query(root << 1 | 1, mid + 1, right, stdl, stdr));
	return ans;
}

void chain_modify(int x, int y, int val) {					
	for (; top[x] != top[y]; x = fa[top[x]]) {		
		if (deep[top[x]] < deep[top[y]])swap(x, y);	
		update(1, 1, n, id[top[x]], id[x], val);
	}
	if (deep[x] < deep[y])swap(x, y);			
	update(1, 1, n, id[y], id[x], val);
}

int main() {
	memset(lazy, -1, sizeof(lazy));
	scanf("%d%d", &n, &m);
	int u, v; LL val;
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &u, &v);
		AddEdge(u, v);
		AddEdge(v, u);
	}
	for (int i = 1; i <= n; i++)
		scanf("%lld", &w[i]);
	dfs1(1, 0, 1);
	dfs2(1, 1);
	build(1, 1, n);
	int rt, opt; scanf("%d", &rt);
	while (m--) {
		scanf("%d", &opt);
		if (opt == 1) scanf("%d", &rt);
		else if (opt == 2) {
			scanf("%d%d", &u, &v);
			scanf("%lld", &val);
			chain_modify(u, v, val);
		}
		else {
			scanf("%d", &u);
			if (rt == u) {
				printf("%lld\n", query(1, 1, n, 1, n));
			}
			else if (id[rt] >= id[u] && id[rt] <= id[u] + n_size[u] - 1) {
				int clo_id = lca2(u, rt);
				printf("%lld\n", min(query(1, 1, n, 1, id[clo_id] - 1), query(1, 1, n, id[clo_id] + n_size[clo_id], n)));
			}
			else
				printf("%lld\n", query(1, 1, n, id[u], id[u] + n_size[u] - 1));
		}
	}
}

List wants to travel代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 40005;
const int maxm = 40005;
const LL mod = 1e9;
int n, m;
int head[maxn], tot;
struct Edge {
	int v;
	int next;
} edge[maxm << 1];
inline void AddEdge(int u, int v) {
	edge[++tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot;
}
int deep[maxn], n_size[maxn], fa[maxn], son[maxn];
//depp为节点深度,n_size为节点大小,fa为节点父亲,son为节点的重儿子
void dfs1(int now, int f, int depth) {	//now当前节点,f父亲,depth深度
	deep[now] = depth;							//标记每个点的深度
	fa[now] = f;								//标记每个点的父亲
	n_size[now] = 1;							//标记每个非叶子节点的子树大小
	for (int i = head[now]; i; i = edge[i].next) {
		if (f == edge[i].v)continue;			//若为父亲则continue
		dfs1(edge[i].v, now, depth + 1);		//dfs其儿子
		n_size[now] += n_size[edge[i].v];		//把它的儿子数加到它身上
		if (n_size[edge[i].v] > n_size[son[now]])//标记每个非叶子节点的重儿子编号
			son[now] = edge[i].v;
	}
}
int top[maxn], id[maxn], dfn;			//链的顶点,节点的dfs序,dfs序编号
int wt[maxn], w[maxn];				//新的点权值,原点的权值
void dfs2(int now, int top_node) {		//now当前节点,top_node当前链的最顶端的节点
	id[now] = ++dfn;					//标记每个点的新编号
	wt[dfn] = w[now];					//把每个点的初始值赋到新编号上来
	top[now] = top_node;				//这个点所在链的顶端
	if (!son[now]) return;				//如果没有儿子则返回
	dfs2(son[now], top_node);			//按先处理重儿子,再处理轻儿子的顺序递归处理
	for (int i = head[now]; i; i = edge[i].next) {
		if (edge[i].v == fa[now] || edge[i].v == son[now])continue;
		dfs2(edge[i].v, edge[i].v);		//对于每一个轻儿子都有一条从它自己开始的链
	}
}

struct Tree {
	int pre;
	int suf;
	int data;
	Tree() {
		pre = suf = data = -1;
	}
	friend Tree operator +(const Tree& a, const Tree& b) {
		if (a.data == -1)return b;
		if (b.data == -1) return a;
		Tree res;
		res.pre = a.pre;
		res.suf = b.suf;
		if (a.suf == b.pre)res.data = a.data + b.data - 1;
		else res.data = a.data + b.data;
		return res;
	}
} tree[maxn << 2];
int lazy[maxn << 2];
void build(int root, int left, int right) {
	lazy[root] = -1;
	if (left == right) {
		tree[root].pre = tree[root].suf = wt[left];
		tree[root].data = 1;
		return;
	}
	int mid = (left + right) >> 1;
	build(root << 1, left, mid);
	build(root << 1 | 1, mid + 1, right);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
void pushdown(int root, int left, int right) {
	if (lazy[root] == -1) return;
	int mid = (left + right) >> 1;
	tree[root << 1].data = 1;
	tree[root << 1].pre = tree[root << 1].suf = lazy[root];
	tree[root << 1 | 1].data = 1;
	tree[root << 1 | 1].pre = tree[root << 1 | 1].suf = lazy[root];
	lazy[root << 1] = lazy[root << 1 | 1] = lazy[root];
	lazy[root] = -1;
}
void update(int root, int left, int right, int stdl, int stdr, int val) {
	if (stdl <= left && right <= stdr) {
		tree[root].data = 1;
		tree[root].pre = tree[root].suf = val;
		lazy[root] = val;
		return;
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	if (stdl <= mid) update(root << 1, left, mid, stdl, stdr, val);
	if (stdr > mid) update(root << 1 | 1, mid + 1, right, stdl, stdr, val);
	tree[root] = tree[root << 1] + tree[root << 1 | 1];
}
Tree query(int root, int left, int right, int stdl, int stdr) {
	if (stdl <= left && right <= stdr) {
		return tree[root];
	}
	pushdown(root, left, right);
	int mid = (left + right) >> 1;
	Tree lson, rson;
	if (stdl <= mid) lson = query(root << 1, left, mid, stdl, stdr);
	if (stdr > mid)  rson = query(root << 1 | 1, mid + 1, right, stdl, stdr);
	return lson + rson;
}
void chain_update(int x, int y, int val) {
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]])swap(x, y);
		update(1, 1, n, id[top[x]], id[x], val);
	}
	if (deep[x] < deep[y])swap(x, y);
	update(1, 1, n, id[y] + 1, id[x], val);
}
int chain_query(int x, int y) {
	int prex = -1, prey = -1, res = 0;
	Tree T;
	for (; top[x] != top[y]; x = fa[top[x]]) {
		if (deep[top[x]] < deep[top[y]]) {
			swap(x, y);
			swap(prex, prey);
		}
		T = query(1, 1, n, id[top[x]], id[x]);
		res += T.data;
		if (T.suf == prex)res--;
		prex = T.pre;
	}
	if (deep[x] < deep[y]) {
		swap(x, y);
		swap(prex, prey);
	}
	if (x == y) {
		if (prex == prey)res--;
		return res;
	}
	T = query(1, 1, n, id[y] + 1, id[x]);
	res += T.data;
	if (prex == T.suf)res--;
	if (prey == T.pre)res--;
	return res;
}

void init() {
	fill(head, head + 1 + n, 0);
	fill(son, son + 1 + n, 0);
	tot = 0;
	dfn = 0;
}

int u[maxn], v[maxn], s[maxn];
int main() {
	while (~scanf("%d%d", &n, &m)) {
		init();
		for (int i = 1; i < n; i++) {
			scanf("%d%d%d", &u[i], &v[i], &s[i]);
			AddEdge(u[i], v[i]);
			AddEdge(v[i], u[i]);
		}
		dfs1(1, 0, 1);
		w[1] = -1;
		for (int i = 1; i < n; i++) {
			if (deep[u[i]] > deep[v[i]])
				swap(u[i], v[i]);
			w[v[i]] = s[i];
		}
		dfs2(1, 1);
		build(1, 1, n);
		int  x, y, z;
		char opt[10];
		while (m--) {
			scanf("%s", opt);
			if (opt[0] == 'C') {
				scanf("%d%d%d", &x, &y, &z);
				chain_update(x, y, z);
			}
			else {
				scanf("%d%d", &x, &y);
				printf("%d\n", x == y ? 0 : chain_query(x, y));
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/qcwlmqy/article/details/97887463