1. 程式人生 > >#DFS序+二分+字首和# Codeforces Round #381 (Div. 1) B. Alyona and a tree

#DFS序+二分+字首和# Codeforces Round #381 (Div. 1) B. Alyona and a tree

題目連結

B. Alyona and a tree

time limit per test  2 seconds

memory limit per test  256 megabytes

input  standard input

output  standard output

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 vcontrols u.

Input

The first line contains single integer n

 (1 ≤ n ≤ 2·105).

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 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 ≤ 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.

 

Description:

給出一顆由n個節點構成的帶權樹,求每個節點可以控制的節點數,v節點控制 u 節點的條件:dist(v, u) <= a[u]。

 

Solution:

假設 dis[x] 表示根節點到 x 節點上的邊權和,則 x 這個節點被 u 節點控制的條件為:dis[x] - dis[u] <= a[x], 即 dis[x] - a[x] <= dis[u]。 由於 dis陣列是通過DFS序來維護的,所以一定是單調遞增的,那麼就可以利用二分找出dis陣列中第一個滿足 dis[x] - a[x] <= dis[u] 的u節點,那麼 u → x 的路徑上的所有節點都能夠控制 x 節點,而 u 節點以上的節點都不能控制x節點,所以在這裡可以引入一個利用字首和記錄答案的思想:假設在當前已找到一條路徑為 u → x ,其中 u 節點的父節點為 fa[u],x節點的父節點為 fa[x],那麼令 ans[fa[x]]++,ans[fa[u]]--(ans[fa[x]]++這一語句可以使用另一種記錄方法:將 ans 陣列在一開始時就賦值為1,然後再最後答案處都減一也可),然後在DFS序的時候累加答案記錄字首和即可。


Implementation:

DFS序維護 dis陣列和記錄 ans陣列的字首和

二分查詢每個節點的合法路徑

字首和(或者是樹上差分思想)維護答案

 

Code:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define Fio ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define fopen freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);
#define mst(a, b) memset(a, b, sizeof(a))
#define _rush() int T; cin >> T; while(T--)
#define rush() int T; scanf("%d", &T); while(T--)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<int, LL> PIL;
typedef pair<LL, int> PLI;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e9 + 7;
const int MaxN = 2e5 + 5;

vector <PIL> G[MaxN];
vector <PLI> path;
LL a[MaxN], dis[MaxN];
int vis[MaxN], ans[MaxN];

void DFS(int x, int fa) {
	vis[x] = 1;
	//ans[x]++;  //選擇在一開始時將ans陣列賦值1
	int pos = lower_bound(path.begin(), path.end(), PLI(dis[x] - a[x], 0)) - path.begin(); //二分查詢第一個合法u節點
	if(--pos >= 0) ans[path[pos].second]--; //u節點的父節點(第一個不合法的節點)減一
	ans[fa]++; //(如果使用一開始將ans賦值為1,不需要這句)當前查詢節點的父節點加一,u —> fa這些節點都可控制當前節點,所以答案加一
	path.push_back(PLI(dis[x], x)); //當前路徑壓入查詢路徑中
	for(int i = 0; i < G[x].size(); i++) {
		int u = G[x][i].first; LL w = G[x][i].second;
		if(vis[u]) continue;
		dis[u] = dis[x] + w;
		DFS(u, x);
		ans[x] += ans[u]; //字首和維護答案
	}
	path.pop_back(); //維護查詢路徑的根節點
}

int main()
{
    Fio;

	int n;
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 2; i <= n; i++) {
		int u; LL d;
		cin >> u >> d;
		G[i].push_back(PIL(u, d));
		G[u].push_back(PIL(i, d));
	}
	DFS(1, 0);
	//for(int i = 1; i <= n; i++) cout << ans[i] - 1 << " ";
	for(int i = 1; i <= n; i++) cout << ans[i] << " ";
	cout << endl;
	return 0;
}