#Codeforces 381 [div2] D. Alyona and a tree 【树链剖分 + 优先队列】

题目:

D. Alyona and a tree

time limit per test  2 seconds

memory limit per test  256 megabytes

input  standard input

output  standard output

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

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

The vertex v controlsthe vertex u (v ≠ u) if and only if u is in thesubtree of v and dist(v, u) ≤ au.

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

Input

The first linecontains single integer n (1 ≤ n ≤ 2·105).

The second linecontains n integers a1, a2, ..., an (1 ≤ ai ≤ 109) — theintegers written in the vertices.

The next (n - 1) linescontain two integers each. The i-th of these lines contains integers pi and wi (1 ≤ pi ≤ n1 ≤ wi ≤ 109) — theparent of the (i + 1)-th vertex in the tree and the number written on the edgebetween pi and (i + 1).

It is guaranteedthat the given graph is a tree.

Output

Print n integers —the i-th of thesenumbers should be equal to the number of vertices that the i-th vertexcontrols.

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 exampletest case the vertex 1 controls the vertex 3, the vertex 3 controlsthe vertex 5 (note that is doesn't mean the vertex 1controls the vertex 5).


首先看出是一道递归求解题目,考虑到数据量很大,且存在点权和边权两种限制因素。故无法直接用树状dp处理。


可以想到,由于从下到上,树是逐渐归并的,所以对于每一个分支,只需维护一个优先队列,每次增加一条边后,将队列里长度小于(之前累加长度+新加入的边权)的点出队(因为如果无法到达这个点,更无法到达其父亲节点)。然后将该节点的权值 + 之前的累加值入队。(即,从这条边开始可以向后继续走 该点权值 的长度)


但如果每个点都开一个优先队列,必然会MLE,而且搬运数据的过程也会超时。

故,我们分情况讨论


首先,给每个节点(结构体)分配一个优先队列指针,方便在不搬运数据的情况下改变其内容。

如果这个节点是叶子节点,我们会为该节点分配一个新的优先队列,并将自身权值入队

如果这个节点只有一个儿子节点,我们会将其 累计边权 累加上这条边的长度。并直接将这个节点的优先队列指针指向其唯一儿子的优先队列。 (注意,处理完以后记着把自己入队)


如果这个节点有多个子节点,把该点的优先队列指针指向其儿子中 优先队列最长的一个(重链)。然后将其余优先队列的归并至该节点中。因为每次都选择最大的作为母版,故复杂度能够控制在 N * LOG N 以内


每个节点的ans 就是 该节点 优先队列的 size


代码:

#define _author "Reskip"

#define _CRT_SECURE_NO_WARNINGS
#include<iostream> 
#include<queue>
#include<set>
#include<algorithm>
#include<vector>
#include<string>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<iomanip>
#include<functional>

using namespace std;

#define maxn 200005

struct node{
	long long dis;
	vector<int>con;
	vector<long long>far;
	long long ans;
	long long cntdis;
	int pq;
}nodes[maxn];

priority_queue<long long, vector<long long>, greater<long long>>que[maxn];
int beg;

int dfs(int x)
{
	int k = nodes[x].con.size();
	if (k == 0)//若为叶子
	{
		nodes[x].pq = beg;
		beg++;
		que[nodes[x].pq].push(nodes[x].dis);
		nodes[x].ans = 0;
		nodes[x].cntdis = 0;
		return nodes[x].pq;
	}
	if (k == 1)//一个儿子
	{
		int temp = dfs(nodes[x].con[0]);
		nodes[x].pq = temp;
		nodes[x].cntdis = nodes[nodes[x].con[0]].cntdis + nodes[x].far[0];
		while (!que[temp].empty() && que[temp].top() < nodes[x].cntdis)
		{
			que[temp].pop();
		}
		nodes[x].ans = que[temp].size();
		que[temp].push(nodes[x].dis + nodes[x].cntdis);
		return temp;
	}
	//nodes[x].pq = beg;
	//beg++;
	//int pqn = beg - 1;
	int smaxn = 0;//多个儿子
	int tmpk;
	for (int i = 0; i < k; i++)
	{
		dfs(nodes[x].con[i]);
		tmpk = que[nodes[nodes[x].con[i]].pq].size();
		if (tmpk > smaxn)
		{
			smaxn = tmpk;
			nodes[x].pq = nodes[nodes[x].con[i]].pq;
			nodes[x].cntdis = nodes[nodes[x].con[i]].cntdis + nodes[x].far[i];
		}
	}

	for (int i = 0; i < k; i++)
	{
		if (nodes[x].pq == nodes[nodes[x].con[i]].pq)
		{
			continue;
		}
		int spq = nodes[nodes[x].con[i]].pq;
		long long sdis = nodes[x].far[i];
		sdis += nodes[nodes[x].con[i]].cntdis;
		while (!que[spq].empty())
		{
			if (que[spq].top() >= sdis)
			{
				que[nodes[x].pq].push(que[spq].top() - sdis + nodes[x].cntdis);
			}
			que[spq].pop();
		}
	}
	while (!que[nodes[x].pq].empty() && que[nodes[x].pq].top() < nodes[x].cntdis)
	{
		que[nodes[x].pq].pop();
	}
	nodes[x].ans = que[nodes[x].pq].size();
	que[nodes[x].pq].push(nodes[x].dis + nodes[x].cntdis);

	return nodes[x].pq;
}

int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		scanf("%I64d", &nodes[i].dis);
	}
	long long op, far;
	for (int i = 2; i <= n; i++)
	{
		scanf("%I64d%I64d", &op, &far);
		nodes[op].con.push_back(i);
		nodes[op].far.push_back(far);
	}
	dfs(1);
	for (int i = 1; i <= n; i++)
	{
		if (i != 1)
		{
			printf(" ");
		}
		printf("%I64d", nodes[i].ans);
	}
	cout << "\n";
	//cin >> n;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值