DFS序、树链剖分题目练习

目录

DFS序:

POJ3321 Apple Tree

HDU3887 Counting Offspring

CF620E New Year Tree

CF383C Propagating tree

树链剖分:

POJ2763 Housewife Wind

HDU2856 How far away ?

CF343D Water Tree

POJ3237 Tree

HDU3966 Aragorn's Story


DFS序:

POJ3321Apple Tree

题意:有一颗苹果树,苹果会长在分叉处,若对某一分叉进行操作:原本有苹果则摘下苹果,否则长一颗苹果。对于每一个询问输出分叉X已经它上面有多少个苹果。

思路:线段树区间求和+单点修改。用DFS序将树转为线性结构,则分叉X上方的分叉区间为:in[x] ~ out[x]。

AC代码:

/*---------------------------------
 *File name: A-dfs序+线段树.cpp
 *Creation date: 2020-06-30 13:35
 *Link: http://poj.org/problem?id=3321
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

struct Edge{
	int v, nxt;
}edge[maxn << 1];
struct Tree{
	int l, r, sum;
}t[maxn << 2];

int head[maxn];
int tot = 0;
int n, m;
int a[maxn], Time;
int in[maxn], out[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].nxt = head[u];
	head[u] = tot++;
}

void Dfs(int x, int pre){
	in[x] = ++Time;
	a[Time] = x;
	for(int i = head[x]; i != -1; i = edge[i].nxt){
		int v = edge[i].v;
		if(v == pre) continue;
		Dfs(v, x);
	}
	out[x] = Time;
}

void Build(int l, int r, int k){
	t[k].l = l, t[k].r = r;
	if(l == r){
		t[k].sum = 1;
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

int Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].sum;
	int mid = l + r >> 1;
	int ans = 0;
	if(L <= mid) ans += Query(l, mid, k << 1, L, R);
	if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
	return ans;
}

void Update(int l, int r, int k, int x){
	if(l == r){
		t[k].sum ^= 1;
		return;
	}
	int mid = l + r >> 1;
	if(x <= mid) Update(l, mid, k << 1, x);
	else Update(mid + 1, r, k << 1 | 1, x);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

int main(){
	memset(head, -1, sizeof head);
	scanf("%d", &n);
	for(int i = 1; i < n; ++i){
		int u, v;
		scanf("%d %d", &u, &v);
		Add_Edge(u, v);
		Add_Edge(v, u);
	}
	Dfs(1, -1);
	Build(1, n, 1);
	scanf("%d", &m);
	while(m--){
		char op;
		int x;
		scanf(" %c %d", &op, &x);
		if(op == 'Q'){
			int l = in[x], r = out[x];
			printf("%d\n", Query(1, n, 1, l, r));
		}
		else{
			x = in[x];
			Update(1, n, 1, x);
		}
	}
	return 0;
}

HDU3887 Counting Offspring

题意:给一颗树,定义函数F( i )为结点 i 的子树中比它小的结点数。输出每个结点的F( i )。

思路:DFS处理原数后用树状数组或线段树处理。由小节点到大节点一个一个单点更新in[node[i]],在更新前计算子树中的区间和。

AC代码:

/*---------------------------------
 *File name: C-Dfs序+线段树.cpp
 *Creation date: 2020-06-30 15:33
 *Link: http://acm.hdu.edu.cn/showproblem.php?pid=3887
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

struct Edge{
	int v, next;
}edge[maxn << 1];
struct Tree{
	int l, r;
	int sum;
	int f;
}t[maxn << 2];

int head[maxn];
int tot;
int Time, a[maxn];
int in[maxn], out[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs(int u, int pre){
	in[u] = ++Time;
	a[Time] = u;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre) continue;
		Dfs(v, u);
	}
	out[u] = Time;
}

int node = 0;
void Build(int l, int r, int k){
	t[k].l = l, t[k].r = r;
	if(l == r){
		t[k].sum = 0;
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].sum = 0;
}

int Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].sum;
	int mid = l + r >> 1;
	int ans = 0;
	if(L <= mid) ans += Query(l, mid, k << 1 , L, R);
	if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
	return ans;
}

void Update(int l, int r, int k, int L, int R){
	if(l == r){
		t[k].sum += 1;
		return;
	}
	int mid = l + r >> 1;
	if(L <= mid) Update(l, mid, k << 1, L, R);
	if(R > mid) Update(mid + 1, r, k << 1 | 1, L, R);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

int main(){
	int n, q;
	while(scanf("%d %d", &n, &q) && n && q){
		for(int i = 1; i <= n; ++i) head[i] = -1;
		tot = 0, Time = 0;
		for(int i = 1; i < n; ++i){
			int u, v;
			scanf("%d %d", &u, &v);
			Add_Edge(u, v);
			Add_Edge(v, u);
		}
		Dfs(q, -1);
		Build(1, n, 1);
		for(int i = 1; i <= n; ++i){
			int l = in[i];
			int r = out[i];
			printf("%d%c", Query(1, n, 1, l, r), i == n ? '\n' : ' ');
			Update(1, n, 1, l, l);
		}
	}
	return 0;
}

CF620 E. New Year Tree

题意:给一颗树,每个结点有初始颜色 C_i,有两种操作,第一种操作为将 V_k 已经它的子树全部涂成 C_k。第二种操作为询问结点V_k 的子树有多少种颜色。

思路:DFS将原树处理成线性结构后,用线段树处理操作,区间修改与区间查询。由于颜色种类数只有60种,所以用一个long long类型的数的二进制作为计算线段树覆盖区间内的不同颜色是否存在。

AC代码:

/*---------------------------------
 *File name: E-DFS序+线段树.cpp
 *Creation date: 2020-07-01 09:35
 *Link: 
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair 
using namespace std;
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;

struct Tree{
	int l, r;
	LL cnt;
	int f;
}t[maxn << 2];

int in[maxn], out[maxn], Time, a[maxn];
int c[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs(int x, int pre){
	in[x] = ++Time;
	a[Time] = x;
	for(int i = head[x]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre) continue;
		Dfs(v, x);
	}
	out[x] = Time;
	return;
}

int node;
void Build(int l, int r, int k){
	t[k].l = l, t[k].r = r;
	if(l == r){
		t[k].cnt = (1LL << c[a[++node]]);
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].cnt = (t[k << 1].cnt | t[k << 1 | 1].cnt);
}

inline void Down(Tree &lch, Tree &rch, int k){
	lch.f = t[k].f;
	lch.cnt = (1LL << t[k].f);
	rch.f = t[k].f;
	rch.cnt = (1LL << t[k].f);
	t[k].f = 0;
}

void Update(int l, int r, int k, int L, int R, int x){
	if(L <= l && r <= R){
		t[k].cnt = (1ll << x);
		t[k].f = x;
		return;
	}
	if(t[k].f) Down(t[k << 1], t[k << 1 | 1], k);
	int mid = l + r >> 1;
	if(L <= mid) Update(l, mid, k << 1, L, R, x);
	if(R > mid) Update(mid + 1, r , k << 1 | 1, L, R, x);
	t[k].cnt = (t[k << 1].cnt | t[k << 1 | 1].cnt);
}

LL Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].cnt;
	if(t[k].f) Down(t[k << 1], t[k << 1 | 1], k);
	int mid = l + r >> 1;
	LL ans = 0;
	if(L <= mid) ans |= Query(l, mid, k << 1, L, R);
	if(R > mid) ans |= Query(mid + 1, r, k << 1 | 1, L, R);
	return ans;
}

int cal(LL x){
	int cnt = 0;
	while(x) cnt += (x & 1), x >>= 1;
	return cnt;
}

int main(){
	int n, m;
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%d", &c[i]), head[i] = -1;
	for(int i = 1; i < n; ++i){
		int u, v;
		scanf("%d %d", &u, &v);
		Add_Edge(u, v);
		Add_Edge(v, u);
	}
	Dfs(1, -1);
	Build(1, n, 1);
	//for(int i = 1; i <= n; ++i) printf("%d: %d %d\n", i, in[i], out[i]);
	while(m--){
		int op;
		scanf("%d", &op);
		if(op == 1){
			int Node, x;
			scanf("%d %d", &Node, &x);
			int l, r;
			l = in[Node];
			r = out[Node];
			Update(1, n, 1, l, r, x);
		}
		else{
			int x;
			scanf("%d", &x);
			int l, r;
			l = in[x];
			r = out[x];
			printf("%d\n", cal(Query(1, n, 1, l, r)));
		}
	}
	return 0;
}

CF383 C. Propagating tree

题意:给定一颗树,在树上进行两种操作。第一种操作为结点X的值增加val,然后结点X的儿子结点的值增加 -val,然后修改结点X的儿子的儿子为 val...直到叶子结点。第二种操作为输出结点X的值。

思路:dfs序处理了原树之后,在树上建线段树。如果规定层数为奇数的层始终是加上val而偶数层始终加上-val,那么只需要直接根据X的层数进行区间修改,在单点查询时也只需要判断当前结点的层数来取正负号。

AC代码:

/*---------------------------------
 *File name: B.cpp
 *Creation date: 2020-07-05 14:21
 *Link: 
 *-------------------------------*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
#define Pque priority_queue 
using namespace std;
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double EPS = 1e-8;

struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;

struct Tree{
	int l, r;
	LL w, f;
}t[maxn << 2];

int val[maxn];
int n, m;

int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int time, id[maxn], rk[maxn], top[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs1(int u, int p, int d){
	pre[u] = p;
	dep[u] = d;
	sz[u] = 1;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs1(v, u, d + 1);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
}

void Dfs2(int u, int root){
	id[u] = ++time;
	rk[time] = u;
	top[u] = root;
	if(!son[u]) return;
	Dfs2(son[u], root);
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre[u]) continue;
		if(v == son[u]) continue;
		Dfs2(v, v);
	}
}	

void Build(int l, int r, int k){
	t[k].r = r, t[k].l = l;
	if(l == r){
		t[k].w = 0;
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].w = t[k << 1].w + t[k << 1 | 1].w;
}

void Down(int k){
	t[k << 1].f += t[k].f;
	t[k << 1 | 1].f += t[k].f;
	t[k << 1].w += (t[k << 1].r - t[k << 1].l + 1) * t[k].f;
	t[k << 1 | 1].w += (t[k << 1 | 1].r - t[k << 1 | 1].l + 1) * t[k].f;
	t[k].f = 0;
}

void Update(int l, int r, int k, int L, int R, int c){
	if(L <= l && r <= R){
		t[k].f += c;
		t[k].w += (r - l + 1) * c;
		return;
	}
	if(t[k].f != 0) Down(k);
	int mid = l + r >> 1;
	if(L <= mid) Update(l, mid, k << 1, L, R, c);
	if(R > mid) Update(mid + 1, r , k << 1 | 1, L, R, c);
	t[k].w = t[k << 1].w + t[k << 1 | 1].w;
}

void Point_Update(int l, int r, int k, int x, int c){
	if(l == r){
		t[k].w += c;
		return;
	}
	if(t[k].f != 0) Down(k);
	int mid = l + r >> 1;
	if(x <= mid) Point_Update(l, mid, k << 1, x, c);
	else Point_Update(mid + 1, r, k << 1 | 1, x, c);
	t[k].w += t[k << 1].w + t[k << 1 | 1].w;
}

LL Query(int l, int r, int k, int x){
	if(l == r) return t[k].w;
	LL ans = 0;
	int mid = l + r >> 1;
	if(t[k].f != 0) Down(k);
	if(x <= mid) ans += Query(l, mid, k << 1, x);
	else ans += Query(mid + 1, r, k << 1 | 1, x);
	return ans;
}

void PRINT(int l, int r, int k){
	if(l == r){
		printf("%d ", t[k].w);
		return;
	}
	if(t[k].f != 0) Down(k);
	int mid = l + r >> 1;
	PRINT(l, mid, k << 1);
	PRINT(mid + 1, r, k << 1 | 1);
}

int main(){
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%d", &val[i]), head[i] = -1;
	for(int i = 1; i < n; ++i){
		int u, v;
		scanf("%d %d", &u, &v);
		Add_Edge(u, v);
		Add_Edge(v, u);
	}
	Dfs1(1, -1, 1);
	Dfs2(1, 1);
	Build(1, n, 1);
	while(m--){
		int op;
		scanf("%d", &op);
		if(op == 1){
			int x, v;
			scanf("%d %d", &x, &v);	
			if(dep[x] % 2 == 0) v *= -1;
			//printf("%d to %d\n", id[x] + 1, id[x] + sz[x] - 1);
			Update(1, n, 1, id[x], id[x] + sz[x] - 1, v);
			//PRINT(1, n, 1); printf("\n");
		}
		else{
			int x;
			scanf("%d", &x);
			LL ans = Query(1, n, 1, id[x]);
			if(dep[x] % 2 == 0) ans *= -1;
			printf("%lld\n", ans + val[x]);
		}
	}
	return 0;
}

树链剖分:

POJ2763 Housewife Wind

题意:给定一个N个顶点的树,有Q次操作,每次操作有两种,第一种是询问当前点到 c 点的边权和。第二种是修改第 i 条边的权值为W。

思路:将边权转化为点权,每条边的权值转化为层数较深的结点的权值,转化后根节点没有权值。按轻重链剖分后的id数组构造线段树,要注意区间查询时的查询区间端点。

AC代码:

/*---------------------------------
 *File name: A.cpp
 *Creation date: 2020-07-05 11:58
 *Link:http://poj.org/problem?id=2763 
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

int n, q, s;
struct EDGES{
	int u, v, w;
}abw[maxn];;
int val[maxn];

struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;

struct Tree{
	int l, r;
	LL w;
}t[maxn << 2];

int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int Time, id[maxn], rk[maxn], top[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs1(int u, int p, int d){
	pre[u] = p;
	dep[u] = d;
	sz[u] = 1;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs1(v, u, d + 1);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
}

void Dfs2(int u, int root){
	id[u] = ++Time;
	rk[Time] = u;
	top[u] = root;
	if(!son[u]) return;
	Dfs2(son[u], root);
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre[u]) continue;
		if(v == son[u]) continue;
		Dfs2(v, v);
	}
}	

void Build(int l, int r, int k){
	t[k].r = r, t[k].l = l;
	if(l == r){
		t[k].w = 0;
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].w = 0;
}

void Update(int l, int r, int k, int x, int c){
	if(l == r){
		t[k].w = c;
		return;
	}
	int mid = l + r >> 1;
	if(x <= mid) Update(l, mid, k << 1, x, c);
	else Update(mid + 1, r, k << 1 | 1, x, c);
	t[k].w = t[k << 1].w + t[k << 1 | 1].w;
}

LL Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].w;
	LL ans = 0;
	int mid = l + r >> 1;
	if(L <= mid) ans += Query(l, mid, k << 1, L, R);
	if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
	return ans;
}

LL cal(int u, int v){
	LL ans = 0;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		ans += Query(1, n, 1, id[top[u]], id[u]);
		u = pre[top[u]];
	}
	if(id[u] > id[v]) swap(u, v);
	if(u == v) return ans;
	ans += Query(1, n, 1, id[son[u]], id[v]);
	return ans;
}

int main(){
	scanf("%d %d %d", &n, &q, &s);
	for(int i = 1; i <= n; ++i) head[i] = -1;
	for(int i = 1; i < n; ++i){
		scanf("%d %d %d", &abw[i].u, &abw[i].v, &abw[i].w);
		Add_Edge(abw[i].u, abw[i].v);
		Add_Edge(abw[i].v, abw[i].u);
	}
	Dfs1(1, -1, 1);
	Dfs2(1, 1);
	Build(1, n, 1);
	for(int i = 1; i < n; ++i){
		int u = abw[i].u;
		int v = abw[i].v;
		int w = abw[i].w;
		if(dep[u] < dep[v]) swap(u, v);
		Update(1, n, 1, id[u], w);
	}
	while(q--){
		int op;
		scanf("%d", &op);
		if(op){
			int i, w;
			scanf("%d %d", &i, &w);
			int u = abw[i].u;
			int v = abw[i].v;
			int c = (dep[u] > dep[v] ? u : v);
			Update(1, n, 1, id[c], w);
		}
		else{
			int x;
			scanf("%d", &x);
			LL ans = cal(s, x);
			printf("%lld\n", ans);
			s = x;
		}
	}
	return 0;
}

HDU2856 How far away ?

题意:给一颗树,问两节点路径距离。

思路:树链剖分+线段树裸题。

AC代码:

/*---------------------------------
 *File name: A.cpp
 *Creation date: 2020-07-05 11:58
 *Link: 
 *-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

int n, m;
struct EDGES{
	int u, v, w;
}abw[maxn];;
int val[maxn];

struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;

struct Tree{
	int l, r;
	LL w;
}t[maxn << 2];

int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int Time, id[maxn], rk[maxn], top[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs1(int u, int p, int d){
	pre[u] = p;
	dep[u] = d;
	sz[u] = 1;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs1(v, u, d + 1);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
}

void Dfs2(int u, int root){
	id[u] = ++Time;
	rk[Time] = u;
	top[u] = root;
	if(!son[u]) return;
	Dfs2(son[u], root);
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre[u]) continue;
		if(v == son[u]) continue;
		Dfs2(v, v);
	}
}	

void Build(int l, int r, int k){
	t[k].r = r, t[k].l = l;
	if(l == r){
		t[k].w = val[rk[l]];
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].w = t[k << 1].w + t[k << 1 | 1].w;
}

LL Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].w;
	LL ans = 0;
	int mid = l + r >> 1;
	if(L <= mid) ans += Query(l, mid, k << 1, L, R);
	if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
	return ans;
}

LL cal(int u, int v){
	LL ans = 0;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		ans += Query(1, n, 1, id[top[u]], id[u]);
		u = pre[top[u]];
	}
	if(id[u] > id[v]) swap(u, v);
	if(u == v) return ans;
	ans += Query(1, n, 1, id[son[u]], id[v]);
	return ans;
}

int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d %d", &n, &m);
		for(int i = 1; i <= n; ++i) head[i] = -1, son[i] = 0, top[i] = 0;
		Time = tot = 0;
		for(int i = 1; i < n; ++i){
			scanf("%d %d %d", &abw[i].u, &abw[i].v, &abw[i].w);
			Add_Edge(abw[i].u, abw[i].v);
			Add_Edge(abw[i].v, abw[i].u);
		}
		Dfs1(1, -1, 1);
		Dfs2(1, 1);
		for(int i = 1; i < n; ++i){
			int u = abw[i].u;
			int v = abw[i].v;
			int w = abw[i].w;
			if(dep[u] < dep[v]) swap(u, v);
			val[u] = w;
		}
		Build(1, n, 1);
		while(m--){
			int x, y;
			scanf("%d %d", &x, &y);
			LL ans = cal(x, y);
			printf("%lld\n", ans);
		}
	}
	return 0;
}

CF343 D. Water Tree

题意:给一颗树,三种操作,第一种为将结点V以及子树中的所有结点都置为1,第二种为将结点V已经V的所有祖先结点置为0,第三种为输出结点V的状态。

思路:树链剖分+线段树裸题。

AC代码:

/*---------------------------------
 *File name: F-树链剖分+线段树.cpp
 *Creation date: 2020-07-03 10:27
 *Link: 
 *-------------------------------*/
#include
#include
#include
using namespace std;
const int maxn = 5e5 + 5;

int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int top[maxn], id[maxn], rk[maxn], time;

struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;
struct Tree{
	int l, r, sum;
	int f;
}t[maxn << 2];

int n, q;

void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs1(int x, int p, int d){
	pre[x] = p;
	dep[x] = d;
	sz[x] = 1;
	for(int i = head[x]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs1(v, x, d + 1);
		sz[x] += sz[v];
		if(sz[v] > sz[son[x]]) son[x] = v;
	}
	return;
}

void Dfs2(int x, int root){
	top[x] = root;
	id[x] = ++time;
	rk[time] = x;
	if(!son[x]) return;
	Dfs2(son[x], root);
	for(int i = head[x]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre[x]) continue;
		if(v == son[x]) continue;
		Dfs2(v, v);
	}
}

void Down(int k){
	int lc = (k << 1);
	int rc = (k << 1 | 1);
	t[lc].f = t[rc].f = t[k].f;
	t[lc].sum = (t[lc].r - t[lc].l + 1) * t[k].f;
	t[rc].sum = (t[rc].r - t[rc].l + 1) * t[k].f;
	t[k].f = -1;
}

void Build(int l, int r, int k){
	t[k].l = l, t[k].r = r;
	t[k].f = -1;
	if(l == r){
		t[k].sum = 0;
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

void Update(int l, int r, int k, int L, int R, int c){
	if(L <= l && r <= R){
		t[k].f = c;
		t[k].sum = (r - l + 1) * c;
		return;
	}
	if(t[k].f != -1) Down(k);
	int mid = l + r >> 1;
	if(L <= mid) Update(l, mid, k << 1, L, R, c);
	if(R > mid) Update(mid + 1, r, k << 1 | 1, L, R, c);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

void Change(int u, int v, int c){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		Update(1, n, 1, id[top[u]], id[u], c);
		u = pre[top[u]];
	}
	if(id[u] > id[v]) swap(u, v);
	Update(1, n, 1, id[u], id[v], c);
}

int	Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].sum;
	if(t[k].f != -1) Down(k);
	int ans = 0;
	int mid = l + r >> 1;
	if(L <= mid) ans += Query(l, mid, k << 1, L, R);
	if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
	return ans;
}

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) head[i] = -1;
	for(int i = 1; i < n; ++i){
		int u, v;
		scanf("%d %d", &u, &v);
		Add_Edge(u, v);
		Add_Edge(v, u);
	}
	Dfs1(1, -1, 1);
	Dfs2(1, 1);
	Build(1, n, 1);
	scanf("%d", &q);
	while(q--){
		int op, x;
		scanf("%d %d", &op, &x);
		if(op == 1){//灌水
			Update(1, n, 1, id[x], id[x] + sz[x] - 1, 1);
		}
		else if(op == 2){//抽水
			Change(1, x, 0);
		}
		else{//提问
			int ans = Query(1, n, 1, id[x], id[x] + sz[x] - 1);
			printf("%d\n", ans == sz[x] ? 1 : 0);
		}
	}
	return 0;
}

POJ 3237Tree

题意:给一颗树,在树上进行三种操作,第一种是将第 i 条边的权值改为v,第二种是将点 a , b之间的路径的权值全部取相反数,第三种是询问点a, b路径的最大值。

思路:树链剖分+线段树。

AC代码:

/*---------------------------------
 *File name: H-树链剖分+线段树.cpp
 *Creation date: 2020-07-04 14:59
 *Link: 
 *-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;

struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;
//-----------------
struct Node{
	int u, v, w;
};
//-----------------
struct Tree{
	int l, r, Max, Min;
	bool flag;
}t[maxn << 2];
//-----------------
int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int id[maxn], rk[maxn], top[maxn], Time;
int n, val[maxn];

inline void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void Dfs1(int u, int p, int d){
	pre[u] = p;
	dep[u] = d;
	sz[u] = 1;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == p) continue;
		Dfs1(v, u, d + 1);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
}

void Dfs2(int u, int root){
	id[u] = ++Time;
	rk[Time] = u;
	top[u] = root;
	if(!son[u]) return;
	Dfs2(son[u], root);
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre[u]) continue;
		if(v == son[u]) continue;
		Dfs2(v, v);
	}
}

void Build(int l, int r, int k){
	t[k].l = l, t[k].r = r;
	if(l == r){
		t[k].Max = val[rk[l]];
		t[k].Min = val[rk[l]];
		t[k].flag = 0;
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].Max = max(t[k << 1].Max, t[k << 1 | 1].Max);
	t[k].Min = min(t[k << 1].Min, t[k << 1 | 1].Min);
	t[k].flag = 0;
}

#define lc (k << 1)
#define rc (k << 1 | 1)

inline void swap_Min_Max(int &x, int &y){
	int Min = -x;
	int Max = -y;
	x = Max;
	y = Min;
}

inline void Down(int k){
	t[rc].flag ^= 1;
	t[lc].flag ^= 1;
	swap_Min_Max(t[lc].Max, t[lc].Min);
	swap_Min_Max(t[rc].Max, t[rc].Min);
	t[k].flag = 0;
}

void Update_Point(int l, int r, int k, int x, int c){
	if(l == r){
		t[k].Max = c;
		t[k].Min = c;
		return;
	}
	if(t[k].flag) Down(k);
	int mid = l + r >> 1;
	if(x <= mid) Update_Point(l, mid, k << 1, x, c);
	else Update_Point(mid + 1, r, k << 1 | 1, x, c);
	t[k].Max = max(t[lc].Max, t[rc].Max);
	t[k].Min = min(t[rc].Min, t[lc].Min);
}

void Update_Ter(int l, int r, int k, int L, int R){
	if(L <= l && r <= R){
		swap_Min_Max(t[k].Max, t[k].Min);
		t[k].flag ^= 1;
		return;
	}
	if(t[k].flag) Down(k);
	int mid = l + r >> 1;
	if(L <= mid) Update_Ter(l, mid, lc, L, R);
	if(R > mid) Update_Ter(mid + 1, r, rc, L, R);
	t[k].Max = max(t[lc].Max, t[rc].Max);
	t[k].Min = min(t[rc].Min, t[lc].Min);
}

void Change(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		Update_Ter(1, n, 1, id[top[u]], id[u]);
		u = pre[top[u]];
	}
	if(id[u] > id[v]) swap(u, v);
	Update_Ter(1, n, 1, id[son[u]], id[v]);
}

int Query(int l, int r, int k, int L, int R){
	if(L <= l && r <= R) return t[k].Max;
	if(t[k].flag) Down(k);
	int mid = l + r >> 1;
	int ans = -inf;
	if(L <= mid) ans = max(ans, Query(l, mid, lc, L, R));
	if(R > mid) ans = max(ans, Query(mid + 1, r, rc, L, R));
	return ans;
}

int Cal(int u, int v){
	int ans = -inf;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		ans = max(ans, Query(1, n, 1, id[top[u]], id[u]));
		u = pre[top[u]];
	}
	if(id[u] > id[v]) swap(u, v);
	ans = max(ans, Query(1, n, 1, id[son[u]], id[v]));
	return ans;
}

int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i) head[i] = -1, son[i] = 0;
		tot = 0;
		Time = 0;
		vector vec;
		for(int i = 1; i < n; ++i){
			int u, v, w;
			scanf("%d %d %d", &u, &v, &w);
			Add_Edge(u, v);
			Add_Edge(v, u);
			vec.pb((Node){u, v, w});
		}
		Dfs1(1, -1, 1);
		Dfs2(1, 1);
		val[1] = 0;
		for(int i = 0; i < n - 1; ++i){
			int u, v, w;
			u = vec[i].u;
			v = vec[i].v;
			w = vec[i].w;
			if(dep[u] < dep[v]) swap(u, v);
			val[u] = w;
		}
		Build(1, n, 1);
		char op[10];
		scanf(" %s", op);
		while(op[0] != 'D'){
			if(op[0] == 'C'){//单点修改
				int i, val;
				scanf("%d %d", &i, &val);
				i--;
				int u = vec[i].u, v = vec[i].v;
				int c = (dep[u] > dep[v] ? u : v);
				Update_Point(1, n, 1, id[c], val);
			}
			else if(op[0] == 'N'){//区间取相反数
				int x, y;
				scanf(" %d %d", &x, &y);
				Change(x, y);
			}
			else{//询问
				int x, y;
				scanf(" %d %d", &x, &y);
				int ans = Cal(x, y);
				printf("%d\n", ans);
			}
			scanf(" %s", op);
		}
	}
	return 0;
}

HDU3966 Aragorn's Story

题意:树剖裸题。

思路:线段树。

AC代码:

/*---------------------------------
 *File name: G-树链剖分+线段树.cpp
 *Creation date: 2020-07-01 11:56
 *Link: 
 *-------------------------------*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
#define Pque priority_queue 
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const double EPS = 1e-8;

int top[maxn], id[maxn], rk[maxn], time;
int pre[maxn], sz[maxn], son[maxn], dep[maxn];
struct Edge{
	int v, next;
}edge[maxn << 1];
int head[maxn], tot;
struct Tree{
	int l, r;
	LL sum;
	int f;
}t[maxn << 2];
int n, m, p, a[maxn];

void Dfs1(int u, int p, int d){
	pre[u] = p;
	dep[u] = d;
	sz[u] = 1;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == pre[u]) continue;
		Dfs1(v, u, d + 1);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;
	}
}

void Dfs2(int u, int root){
	top[u] = root;
	id[u] = ++time;
	rk[time] = u;
	if(!son[u]) return;
	Dfs2(son[u], root);
	for(int i = head[u]; i != -1; i = edge[i].next){
		int v = edge[i].v;
		if(v == son[u]) continue;
		if(v == pre[u]) continue;
		Dfs2(v, v);
	}
}

void Add_Edge(int u, int v){
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

inline void Down(int k){
	int lc = (k << 1);
	int rc = (k << 1 | 1);
	t[lc].f += t[k].f;
	t[rc].f += t[k].f;
	t[lc].sum += (t[lc].r - t[lc].l + 1) * 1LL * t[k].f;
	t[rc].sum += (t[rc].r - t[rc].l + 1) * 1LL * t[k].f;
	t[k].f = 0;
}

void Build(int l, int r, int k){
	t[k].l = l, t[k].r = r;
	t[k].f = 0;
	if(l == r){
		t[k].sum = a[rk[l]];
		return;
	}
	int mid = l + r >> 1;
	Build(l, mid, k << 1);
	Build(mid + 1, r, k << 1 | 1);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

void Update(int l, int r, int k, int L, int R, int c){
	if(L <= l && r <= R){
		t[k].f += c;
		t[k].sum += (r - l + 1) * 1LL * c;
		return;
	}
	if(t[k].f != 0) Down(k);
	int mid = l + r >> 1;
	if(L <= mid) Update(l, mid, k << 1, L, R, c);
	if(R > mid) Update(mid + 1, r, k << 1 | 1, L, R, c);
	t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}

void Change(int u, int v, int c){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		Update(1, n, 1, id[top[u]], id[u], c);
		u = pre[top[u]];
	}
	if(id[u] > id[v]) swap(u, v);
	Update(1, n, 1, id[u], id[v], c);
}

LL Query(int l, int r, int k, int x){
	if(l == r){
		return t[k].sum;
	}
	if(t[k].f != 0) Down(k);
	int mid = l + r >> 1;
	if(x <= mid) return Query(l, mid, k << 1, x);
	else return Query(mid + 1, r, k << 1 | 1, x);
}

int main(){
	while(~scanf("%d %d %d", &n, &m, &p)){
		tot = time = 0;
		memset(son, 0, sizeof(son));
		for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), head[i] = -1;
		for(int i =	1; i <= m; ++i){
			int u, v;
			scanf("%d %d", &u, &v);
			Add_Edge(u, v);
			Add_Edge(v, u);
		}
		Dfs1(1, -1, 1);
		Dfs2(1, 1);
		Build(1, n, 1);
		while(p--){
			char op;
			scanf(" %c", &op);
			if(op == 'I' || op == 'D'){
				int x, y, k;
				scanf("%d %d %d", &x, &y, &k);
				if(op == 'D') k = -k;
				Change(x, y, k);
			}
			else{
				int x;
				scanf("%d", &x);
				LL ans = Query(1, n, 1, id[x]);
				printf("%lld\n", ans);
			}
		}
	}
	return 0;
}

 

你可能感兴趣的:(题解,练习)