树形结构练习总结

  • 这是我平生第一次在一天之内做完了一套vjudge上的练习~~(实际上是因为太简单了,大佬根本都不屑于去做)~~
  • 虽然做完了,感觉问题还是很大。在想算法的时候我总是不相信自己的判断,一定要等看到正解思路和我差不多才敢打,或者有的时候要看到了题解的大致方向才能YY出方法。具体体现为:第一题明明想出了AC方法,却以为自己想了个暴力;后面有的题又总是以为自己想的太简单了;还有看了别人的思路才会做的F题。
  • 做难题还是不够熟练,需要多多努力

A (CF686D)

After the piece of a devilish mirror hit the Kay’s eye, he is no longer interested in the beauty of the roses. Now he likes to watch snowflakes.

Once upon a time, he found a huge snowflake that has a form of the tree (connected acyclic graph) consisting of n nodes. The root of tree has index 1. Kay is very interested in the structure of this tree.

After doing some research he formed q q q queries he is interested in. The i i i-th query asks to find a centroid of the subtree of the node v i v_i vi. Your goal is to answer all queries.

Subtree of a node is a part of tree consisting of this node and all it’s descendants (direct or not). In other words, subtree of node v is formed by nodes u u u, such that node v v v is present on the path from u to root.

Centroid of a tree (or a subtree) is a node, such that if we erase it from the tree, the maximum size of the connected component will be at least two times smaller than the size of the initial tree (or a subtree).

Input
The first line of the input contains two integers n n n and q q q (2 ≤  n n n ≤ 300 000, 1 ≤  q q q ≤ 300 000) — the size of the initial tree and the number of queries respectively.

The second line contains n   −   1 n - 1 n1 integer p 2 p2 p2,  p 3 p3 p3,  ⋯ \cdots ,  p n pn pn (1 ≤  p i pi pi ≤  n n n) — the indices of the parents of the nodes from 2 to n. Node 1 is a root of the tree. It’s guaranteed that pi define a correct tree.

Each of the following q lines contain a single integer v i v_i vi (1 ≤  v i v_i vi ≤  n n n) — the index of the node, that define the subtree, for which we want to find a centroid.

Output
For each query print the index of a centroid of the corresponding subtree. If there are many suitable nodes, print any of them. It’s guaranteed, that each subtree has at least one centroid.

Example
Input
7 4
1 1 3 3 5 3
1
2
3
5
Output
3
2
3
6
就放英文题目了。读题还是很重要的,有英语题目要多读读。
题意:

  • 给一棵树,求若干棵子树的重心。

思路:

  • 求重心的方法挺简单的,对每一个节点 u u u,记录一个 f a [ u ] fa[u] fa[u]表示父亲,一个 m a x s u b [ u ] maxsub[u] maxsub[u]表示 u u u的子树大小的最大值,一个 s z [ u ] sz[u] sz[u]表示 u u u u u u为根的子树大小,然后 c t r [ u ] ctr[u] ctr[u]表示重心坐标。
  • 求一棵树的重心挺方便的,主要问题是求多颗。于是可以想从儿子的重心去推父亲的重心,从而减小复杂度。我们可以把从某棵子树推到父亲为根的树看做在那棵子树的根上连上了另一棵树,于是当前子树的重心必定是向根的方向移动的。所以在更新一个节点 u u u时,可以用他儿子子树的重心向上爬,一直到某个儿子的重心爬到了符合要求的地方,那个节点就是 u u u的重心。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N = 3e5+10;
int n, q, fa[N];
vector<int> to[N];
int sz[N], maxsub[N], ctr[N];

void DFS(int u)
{
	maxsub[u] = 0;
	sz[u] = 1;
	if ((u == 1 && to[u].size() == 0) || (u != 1 && to[u].size() == 1)){
		ctr[u] = u;
		return;
	}
	for (int i = 0; i < to[u].size(); i++){
		int v = to[u][i];
		if (v == fa[u]) continue;
		DFS(v);
		sz[u] += sz[v];
		maxsub[u] = max(maxsub[u], sz[v]);
	}
	for (int i = 0; i < to[u].size(); i++){
		int v = to[u][i];
		if (v == fa[u]) continue;
		int now = ctr[v];
		bool fl = 0;
		while (1){//往上爬
			if (max(maxsub[now], sz[u]-sz[now]) <= sz[u]/2){
				fl = 1;
				break;
			}
			if (now == u) break;
			now = fa[now];
		}
		if (fl){
			ctr[u] = now;
			break;
		}
	}
}

int main()
{
	scanf("%d%d", &n, &q);
	for (int i = 2; i <= n; i++){
		scanf("%d", &fa[i]);
		to[i].push_back(fa[i]);
		to[fa[i]].push_back(i);
	}
	DFS(1);
	while (q--){
		int x;
		scanf("%d", &x);
		printf("%d\n", ctr[x]);
	}
	return 0;
}

B (CF191C)

They say that Berland has exactly two problems, fools and roads. Besides, Berland has n cities, populated by the fools and connected by the roads. All Berland roads are bidirectional. As there are many fools in Berland, between each pair of cities there is a path (or else the fools would get upset). Also, between each pair of cities there is no more than one simple path (or else the fools would get lost).

But that is not the end of Berland’s special features. In this country fools sometimes visit each other and thus spoil the roads. The fools aren’t very smart, so they always use only the simple paths.

A simple path is the path which goes through every Berland city not more than once.

The Berland government knows the paths which the fools use. Help the government count for each road, how many distinct fools can go on it.

Note how the fools’ paths are given in the input.

Input
The first line contains a single integer n (2 ≤ n ≤  1 0 5 10^5 105) — the number of cities.

Each of the next n - 1 lines contains two space-separated integers ui, vi (1 ≤ ui, vi ≤ n, ui ≠ vi), that means that there is a road connecting cities ui and vi.

The next line contains integer k (0 ≤ k ≤  1 0 5 10^5 105) — the number of pairs of fools who visit each other.

Next k lines contain two space-separated numbers. The i-th line (i > 0) contains numbers ai, bi (1 ≤ ai, bi ≤ n). That means that the fool number 2i - 1 lives in city ai and visits the fool number 2i, who lives in city bi. The given pairs describe simple paths, because between every pair of cities there is only one simple path.

Output
Print n - 1 integer. The integers should be separated by spaces. The i-th number should equal the number of fools who can go on the i-th road. The roads are numbered starting from one in the order, in which they occur in the input.

Examples
Input
5
1 2
1 3
2 4
2 5
2
1 4
3 5
Output
2 1 1 1
Input
5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
Output
3 1 1 1
全果树上差分+LCA,唯一一道打的超级自信的题,洛谷上也有好多类似的题(P3258)
就放个题留个纪念~~(拒绝放代码)~~

C(CF842C)

Ilya is very fond of graphs, especially trees. During his last trip to the forest Ilya found a very interesting tree rooted at vertex 1. There is an integer number written on each vertex of the tree; the number written on vertex i is equal to ai.

Ilya believes that the beauty of the vertex x is the greatest common divisor of all numbers written on the vertices on the path from the root to x, including this vertex itself. In addition, Ilya can change the number in one arbitrary vertex to 0 or leave all vertices unchanged. Now for each vertex Ilya wants to know the maximum possible beauty it can have.

For each vertex the answer must be considered independently.

The beauty of the root equals to number written on it.

Input
First line contains one integer number n — the number of vertices in tree (1 ≤ n ≤ 2·105).

Next line contains n integer numbers ai (1 ≤ i ≤ n, 1 ≤ ai ≤ 2·105).

Each of next n - 1 lines contains two integer numbers x and y (1 ≤ x, y ≤ n, x ≠ y), which means that there is an edge (x, y) in the tree.

Output
Output n numbers separated by spaces, where i-th number equals to maximum possible beauty of vertex i.

Examples
Input
2
6 2
1 2
Output
6 6
Input
3
6 2 3
1 2
1 3
Output
6 6 6
Input
1
10
Output
10
题意:

  • 求每个节点到根的路径上所有数的GCD,可以在这条路上把一个点变成0,求最大GCD。

思路:

  • 比较暴力的:每个节点开一个set,记录若去掉一个数根到当前点的所有可能的GCD,然后一个 f [ ] f[] f[]数组记录不变一个点的GCD。于是每个点的状态可以从父节点转移过来,有三种选择:
  1. 把自己变成0,则把父节点f值和自己的值取GCD塞进set
  2. 自己不变0,到根的那条链上取一个数变0,则把父节点的set里每个数取GCD塞进set里
  3. 自己不变0,到根的那条链上也没有一个数变0,f[v] = GCD(f[u] , a[v])
  • 好像我也不知道最后set里存了多少数,反正不会很多就对了。。。

  • 还有一个 O ( n n ) O(n\sqrt{n}) O(nn )的方法。用一个sum[i]数组存当前链上有因数i的节点数量,在进入节点的时候++,回溯的时候 − − -- 。然后如果有一个因子出现了depth-1次,他就是可以作为当前点的GCD的,取这些可行的数里面最大的就是这个点的答案了。

//用set的打法,后面那种没有试过,网上看来貌似是对的吧
//set真提莫好用
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
using namespace std;
const int N = 2e5+10;
int n, a[N], dp[N];
vector<int> to[N];
set<int> s[N];

int GCD(int x, int y)
{
	if (y == 0) return x;
	return GCD(y, x%y);
}

void DFS(int u, int fa)
{
	for (int i = 0; i < to[u].size(); i++){
		int v = to[u][i];
		if (v == fa) continue;
		dp[v] = GCD(dp[u], a[v]);
		s[v].insert(dp[u]);//set的插入
		for (set<int>::iterator i = s[u].begin(); i != s[u].end(); i++)//set的遍历
			s[v].insert(GCD(a[v], *i));
		DFS(v, u);
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for (int i = 1; i < n; i++){
		int x, y;
		scanf("%d%d", &x, &y);
		to[x].push_back(y);
		to[y].push_back(x);
	}
	dp[1] = a[1];
	s[1].insert(0);
	s[1].insert(a[1]);
	DFS(1, 0);
	for (int i = 1; i <= n; i++)
		printf("%d ", *s[i].rbegin());//set的反向第一个元素(就是最后一个),还是保证排好序的,也就是最大的
	return 0;
}

D(CF813C)

Alice got tired of playing the tag game by the usual rules so she offered Bob a little modification to it. Now the game should be played on an undirected rooted tree of n vertices. Vertex 1 is the root of the tree.

Alice starts at vertex 1 and Bob starts at vertex x (x ≠ 1). The moves are made in turns, Bob goes first. In one move one can either stay at the current vertex or travel to the neighbouring one.

The game ends when Alice goes to the same vertex where Bob is standing. Alice wants to minimize the total number of moves and Bob wants to maximize it.

You should write a program which will determine how many moves will the game last.

Input
The first line contains two integer numbers n and x (2 ≤ n ≤ 2·105, 2 ≤ x ≤ n).

Each of the next n - 1 lines contains two integer numbers a and b (1 ≤ a, b ≤ n) — edges of the tree. It is guaranteed that the edges form a valid tree.

Output
Print the total number of moves Alice and Bob will make.

Examples
Input
4 3
1 2
2 3
2 4
Output
4
Input
5 2
1 2
2 3
3 4
2 5
Output
6
Note
In the first example the tree looks like this:
这里写图片描述

The red vertex is Alice’s starting position, the blue one is Bob’s. Bob will make the game run the longest by standing at the vertex 3 during all the game. So here are the moves:

B: stay at vertex 3

A: go to vertex 2

B: stay at vertex 3

A: go to vertex 3

In the second example the tree looks like this:
这里写图片描述

The moves in the optimal strategy are:

B: go to vertex 3

A: go to vertex 2

B: go to vertex 4

A: go to vertex 3

B: stay at vertex 4

A: go to vertex 4
博弈的题,两遍遍历,分别搞出alice和bob到每个点的距离,如果alice到某个点的距离大于(不能等于)bob的,那bob就可以跑到这里避难(至于等于,bob不走这一步比走这一步要更优,而且等于可能出现路径的重复,那bob在还没走到之前就被抓了)。于是让bob挑一个可行的避难点里alice走的路最长的就好了。

E

Alyona decided to go on a diet and went to the forest to get some apples. There she unexpectedly found a magic rooted tree with root in the vertex 1, every vertex and every edge of which has a number written on.

The girl noticed that some of the tree’s vertices are sad, so she decided to play with them. Let’s call vertex v sad if there is a vertex u in subtree of vertex v such that dist(v, u) > au, where au is the number written on vertex u, dist(v, u) is the sum of the numbers written on the edges on the path from v to u.

Leaves of a tree are vertices connected to a single vertex by a single edge, but the root of a tree is a leaf if and only if the tree consists of a single vertex — root.

Thus Alyona decided to remove some of tree leaves until there will be no any sad vertex left in the tree. What is the minimum number of leaves Alyona needs to remove?

Input
In the first line of the input integer n (1 ≤ n ≤  1 0 5 10^5 105) is given — the number of vertices in the tree.

In the second line the sequence of n integers a1, a2, …, an (1 ≤ ai ≤  1 0 9 10^9 109) is given, where ai is the number written on vertex i.

The next n - 1 lines describe tree edges: ith of them consists of two integers pi and ci (1 ≤ pi ≤ n,  -  1 0 9 10^9 109 ≤ ci ≤  1 0 9 10^9 109), meaning that there is an edge connecting vertices i + 1 and pi with number ci written on it.

Output
Print the only integer — the minimum number of leaves Alyona needs to remove such that there will be no any sad vertex left in the tree.

Example
Input
9
88 22 83 14 95 91 98 53 11
3 24
7 -8
1 67
1 64
9 65
5 12
6 -80
3 8
Output
5
Note
The following image represents possible process of removing leaves from the tree:
这里写图片描述

题意:去掉一些叶子(去掉之后成为叶子也可以去),使得对于每一个v和他的子树中的u,dist(v, u) <= a[u]
思路:记录从当前点出发往根的方向走的路径中权值最大的那条(不一定连到根),每一次只要路径权值大于a[u],就把这整棵子树去掉,一遍dfs搞定

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
using namespace std;
const int N = 1e5+10;
int n, a[N], s[N];
vector<int> to[N];
vector<int> val[N];
bool col[N];

void DFS(int u, int fa)
{
	col[u] = 1;
	for (int i = 0; i < to[u].size(); i++){
		int v = to[u][i];
		int w = val[u][i];
		if (v == fa) continue;
		s[v] = max(w, s[u]+w);
		if (s[v] > a[v]) continue;
		DFS(v, u);
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	for (int i = 2; i <= n; i++){
		int x, c;
		scanf("%d%d", &x, &c);
		to[x].push_back(i);
		to[i].push_back(x);
		val[x].push_back(c);
		val[i].push_back(c);
	}
	memset(col, 0, sizeof(col));
	s[1] = 0;
	DFS(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++)
		if (!col[i])
			ans++;
	printf("%d", ans);
	return 0;
}

F(CF337D)

Paladin Manao caught the trail of the ancient Book of Evil in a swampy area. This area contains n settlements numbered from 1 to n. Moving through the swamp is very difficult, so people tramped exactly n - 1 paths. Each of these paths connects some pair of settlements and is bidirectional. Moreover, it is possible to reach any settlement from any other one by traversing one or several paths.

The distance between two settlements is the minimum number of paths that have to be crossed to get from one settlement to the other one. Manao knows that the Book of Evil has got a damage range d. This means that if the Book of Evil is located in some settlement, its damage (for example, emergence of ghosts and werewolves) affects other settlements at distance d or less from the settlement where the Book resides.

Manao has heard of m settlements affected by the Book of Evil. Their numbers are p1, p2, …, pm. Note that the Book may be affecting other settlements as well, but this has not been detected yet. Manao wants to determine which settlements may contain the Book. Help him with this difficult task.

Input
The first line contains three space-separated integers n, m and d (1 ≤ m ≤ n ≤ 100000; 0 ≤ d ≤ n - 1). The second line contains m distinct space-separated integers p1, p2, …, pm (1 ≤ pi ≤ n). Then n - 1 lines follow, each line describes a path made in the area. A path is described by a pair of space-separated integers ai and bi representing the ends of this path.

Output
Print a single number — the number of settlements that may contain the Book of Evil. It is possible that Manao received some controversial information and there is no settlement that may contain the Book. In such case, print 0.

Examples
Input
6 2 3
1 2
1 5
2 3
3 4
4 5
5 6
Output
3
Note
Sample 1. The damage range of the Book of Evil equals 3 and its effects have been noticed in settlements 1 and 2. Thus, it can be in settlements 3, 4 or 5.

题意:给一棵树,几个被标记的点,求有多少节点可以到任意一个被标记的点距离小于等于d(定值)。
思路:暴力肯定炸。然后很容易想到某个点要是可行,那他到离他最远的被标记的点的距离<=d。然而这时我还在YY什么树上点分治之类我不会的东西,丝毫没有想到树形DP。最后是看到了树形DP的标签才往这里想。具体思路见备注。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N = 1e5+10;
int n, m, d;
bool ghost[N];
vector<int> to[N];
int f[N], id[N], g[N];//f[i]:i的子树内到i最远距离,id[i]最远的点在哪棵子树里,g[i]非子树最远距离
//f[i]和g[i]都是包含i自己的

void DFS1(int u, int fa)//第一遍DFS处理出f[]和id[]
{
	if (ghost[u]) f[u] = 0;
	 for (int i = 0; i < to[u].size(); i++){
	 	int v = to[u][i];
	 	if (v == fa) continue;
	 	DFS1(v, u);
	 	if (f[v] != -1 && f[u] < f[v]+1)
	 		f[u] = f[v]+1, id[u] = v;
	 }
}

void DFS2(int u, int fa)//第二遍g[],从父亲和同父兄弟那里更新
{
	if (ghost[u]) g[u] = max(g[u], 0);
	int maxf = -1;
	for (int i = 0; i < to[u].size(); i++){//最大距离的点不在此子树中,直接继承
		int v = to[u][i];
		if (v == fa) continue;
		if (g[u] != -1) g[v] = max(g[v], g[u]+1);//顺便把从父亲继承的也做掉
		if (v == id[u]) continue;
		maxf = max(maxf, f[v]);
		g[v] = max(g[v], f[u]+1);
		DFS2(v, u);
	}
	if (id[u] != -1){//前面记了id[u]之外的子树最大距离,用来更新id[u]
		if (maxf != -1) g[id[u]] = max(maxf+2, g[id[u]]);
		DFS2(id[u], u);
	}
}

int main()
{
	scanf("%d%d%d", &n, &m, &d);
	for (int i = 1; i <= m; i++){
		int x;
		scanf("%d", &x);
		ghost[x] = 1;
	}
	for (int i = 1; i < n; i++){
		int x, y;
		scanf("%d%d", &x, &y);
		to[x].push_back(y);
		to[y].push_back(x);
	}
	memset(f, -1, sizeof(f));
	memset(id, -1, sizeof(id));
	DFS1(1, 0);
	memset(g, -1, sizeof(g));
	DFS2(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++)
		if (max(f[i], g[i]) <= d)
			ans++;
	printf("%d\n", ans);
	return 0;
}

G(CF782C)

Andryusha goes through a park each day. The squares and paths between them look boring to Andryusha, so he decided to decorate them.

The park consists of n squares connected with (n - 1) bidirectional paths in such a way that any square is reachable from any other using these paths. Andryusha decided to hang a colored balloon at each of the squares. The baloons’ colors are described by positive integers, starting from 1. In order to make the park varicolored, Andryusha wants to choose the colors in a special way. More precisely, he wants to use such colors that if a, b and c are distinct squares that a and b have a direct path between them, and b and c have a direct path between them, then balloon colors on these three squares are distinct.

Andryusha wants to use as little different colors as possible. Help him to choose the colors!

Input
The first line contains single integer n (3 ≤ n ≤ 2·105) — the number of squares in the park.

Each of the next (n - 1) lines contains two integers x and y (1 ≤ x, y ≤ n) — the indices of two squares directly connected by a path.

It is guaranteed that any square is reachable from any other using the paths.

Output
In the first line print single integer k — the minimum number of colors Andryusha has to use.

In the second line print n integers, the i-th of them should be equal to the balloon color on the i-th square. Each of these numbers should be within range from 1 to k.

Examples
Input
3
2 3
1 3
Output
3
1 3 2
Input
5
2 3
5 3
4 3
1 3
Output
5
1 3 2 5 4
Input
5
2 1
3 2
4 3
5 4
Output
3
1 2 3 1 2
Note
In the first sample the park consists of three squares: 1 → 3 → 2. Thus, the balloon colors have to be distinct.
这里写图片描述

Illustration for the first sample.
In the second example there are following triples of consequently connected squares:
这里写图片描述

1 → 3 → 2
1 → 3 → 4
1 → 3 → 5
2 → 3 → 4
2 → 3 → 5
4 → 3 → 5
We can see that each pair of squares is encountered in some triple, so all colors have to be distinct.
Illustration for the second sample.
In the third example there are following triples:
这里写图片描述

1 → 2 → 3
2 → 3 → 4
3 → 4 → 5
We can see that one or two colors is not enough, but there is an answer that uses three colors only.
Illustration for the third sample.
一道div2C题我看了半天没思路。首先题目看错,把a path理解成有a条路(a是变量),然后看着题解都一脸懵逼。理解题意之后(随便YY)可以知道颜色数就是度最大的点的度+1,因为每一个点和他直接连着的点颜色互不相同,需要度数+1的颜色来涂,取最大即可。接下来是给出方案(我感觉还是先YY出了方案才找到最小颜色数)。DFS,只要不与父亲,祖父,同父兄弟颜色相同就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N = 2e5+10;
int n, dgr[N];
vector<int> to[N];
int col[N], cnum;

void DFS(int u, int fa)
{
//	cout << u << "?" << endl;
	int now = 0;
	for (int i = 0; i < to[u].size(); i++){
		int v = to[u][i];
		if (v == fa) continue;
		now++;//不和同父兄弟相同
		while (now == col[fa] || now == col[u]) now++;//不和父亲或祖父相同
		col[v] = now;
	}
	for (int i = 0; i < to[u].size(); i++){
		int v = to[u][i];
		if (v != fa) DFS(v, u);
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i < n; i++){
		int x, y;
		scanf("%d%d", &x, &y);
		to[x].push_back(y);
		to[y].push_back(x);
		dgr[x]++; dgr[y]++;
	}
	int maxdgr = 0, id = 1;
	for (int i = 1; i <= n; i++)
		if (dgr[i] > maxdgr)
			maxdgr = dgr[i], id = i;
	col[id] = 1;
	DFS(id, 0);
	printf("%d\n", maxdgr+1);
	for (int i = 1; i <= n; i++)
		printf("%d ", col[i]);
	return 0;
}

H

Alyona has a tree with n vertices. The root of the tree is the vertex 1. In each vertex Alyona wrote an positive integer, in the vertex i she wrote ai. Moreover, the girl wrote a positive integer to every edge of the tree (possibly, different integers on different edges).

Let’s define dist(v, u) as the sum of the integers written on the edges of the simple path from v to u.

The vertex v controls the vertex u (v ≠ u) if and only if u is in the subtree of v and dist(v, u) ≤ au.

Alyona wants to settle in some vertex. In order to do this, she wants to know for each vertex v what is the number of vertices u such that v controls u.

Input
The first line contains single integer n (1 ≤ n ≤  2 ∗ 1 0 5 2*10^5 2105).

The second line contains n integers a1, a2, …, an (1 ≤ ai ≤  1 0 9 10^9 109) — the integers written in the vertices.

The next (n - 1) lines contain two integers each. The i-th of these lines contains integers pi and wi (1 ≤ pi ≤ n, 1 ≤ wi ≤  1 0 9 10^9 109) — the parent of the (i + 1)-th vertex in the tree and the number written on the edge between pi and (i + 1).

It is guaranteed that the given graph is a tree.

Output
Print n integers — the i-th of these numbers should be equal to the number of vertices that the i-th vertex controls.

Examples
Input
5
2 5 1 4 6
1 7
1 1
3 5
3 6
Output
1 0 1 0 0
Input
5
9 7 8 6 5
1 1
2 1
3 1
4 1
Output
4 3 2 1 0
Note
In the example test case the vertex 1 controls the vertex 3, the vertex 3 controls the vertex 5 (note that is doesn’t mean the vertex 1 controls the vertex 5).
题目大意是给出一棵有根树,树上每个点、每条边都有一个权值。现在给出“控制”的定义:对一个点u,设点v在其子树上,且,则称u控制v。要求求出每个点控制了多少个点。
因为所有边权都是正整数,所以对于某一个点,到根的路径上的点到他的距离是单调递增的,能控制他的点是一条链, 一边连着他自己,另一边指向根的方向。这符合二分的单调性要求,然而在树上倍增写起来比二分舒服,那就倍增吧。对于这一条链上的点,每个都需要+1 (s),于是想到差分。

//看起来方法挺难得,程序实现起来好短
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define int long long
using namespace std;
const int N = 2e5+10;
int n, a[N];
struct EDGE{
	int nxt, v, w;
}edge[N*2];
int point[N], e;
int f[N][19], g[N][19], d[N];

void add_edge(int u, int v, int w)
{
	edge[++e] = (EDGE){point[u], v, w};
	point[u] = e;
}

void DFS(int u, int fa)
{
	for (int i = 1; i <= 18; i++)
		f[u][i] = f[f[u][i-1]][i-1], g[u][i] = g[u][i-1]+g[f[u][i-1]][i-1];
	for (int i = point[u]; i != -1; i = edge[i].nxt){
		int v = edge[i].v;
		int w = edge[i].w;
		if (v == fa) continue;
		f[v][0] = u;
		g[v][0] = w;
		DFS(v, u);
	}
}

void DFS1(int u, int fa)
{
	for (int i = point[u]; i != -1; i = edge[i].nxt){
		int v = edge[i].v;
//		cout << v << "!" << endl;
		if (v == fa) continue;
//		cout << "@" << endl;
		DFS1(v, u);
		d[u] += d[v];
	}
//	cout << u << " " << d[u] << endl;
}

main()
{
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	memset(point, -1, sizeof(point)); e = 0;
	for (int i = 2; i <= n; i++){
		int x, c;
		scanf("%lld%lld", &x, &c);
		add_edge(x, i, c);
		add_edge(i, x, c);
	}
	f[1][0] = g[1][0] = 0;
	DFS(1, 0);
	memset(d, 0, sizeof(d));
	for (int i = 1; i <= n; i++){
		int u = i, sum = 0;
//		for (int j = 20; j >= 0; j--)
//			cout << g[u][j] << " "; cout << endl;
		for (int j = 18; j >= 0; j--)
			if (f[u][j] != 0 && sum+g[u][j] <= a[i])
				sum += g[u][j], u = f[u][j];
		d[f[i][0]]++; d[f[u][0]]--;
//		cout << d[f[i][0]] << " " << d[f[u][0]] << "!" << endl;
	}
	DFS1(1, 0);
	for (int i = 1; i <= n; i++)
		printf("%lld ", d[i]);
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值