1. 程式人生 > >B - Boatherds POJ - 2114 (樹上點分治)

B - Boatherds POJ - 2114 (樹上點分治)

Boatherds Inc. is a sailing company operating in the country of Trabantustan and offering boat trips on Trabantian rivers. All the rivers originate somewhere in the mountains and on their way down to the lowlands they gradually join and finally the single resulting river flows to the sea. Moreover, the Trabantian villages are exactly at the rivers' springs, junctions and at the mouth of the largest river. Please note that more than 2 rivers can join at a junction. However, the rivers always form a tree (with villages as vertices). 

The pricing policy of the Boatherds is very simple: each segment of each river between two villages is assigned a price (the price is same in both directions), so if a tourist requests a journey between any two villages, the ticket office clerks just add the prices of the segments along the only path between the villages. 

One day, a very strange tourist appeared. She told the clerks that she returns to her country on the next day and she wants to spend all the remaining money on a boat trip, so they should find a route with exactly this cost. Being just poor (ahem) businessmen, they have asked the Abacus Calculator Makers for help. 

You are given a description of the river network with costs of river segments and a sequence of integers x1,..., xk. For each xi, you should determine if there is a pair of cities (a, b) in the river network such that the cost of the trip between a and b is exactly xi. 

Input

The input consists of several instances. Each instance is described by (in the following order): 

  • A single line containing a single integer: the number of villages N (1 <= N <= 10 000). 
  • N lines describing the villages. The i-th of these lines (1 <= i <= N) describes the village with number i. It contains space separated integers d1, c1, d2, c2, , dki, cki, 0. The dj's are numbers of villages from which the rivers flow directly to the village i (with no other villages in between), each cj is the price of the journey between villages i and dj. Moreover, 2 <= dj <= N and 0 <= cj <= 1 000. Village 1 always corresponds to the mouth of the largest river, therefore no di can ever be equal to 1. 
  • M <= 100 lines describing the queries. The i-th of these lines corresponds to the i-th query and contains a single integer xi (1 <= xi <= 10 000 000). 
  • The instance is finished by a single line containing the number 0.


The whole input is ended by a single line containing the number 0. 

Output

For each instance you should produce a sequence of M lines (where M is the number of queries in the particular instance). The i-th of these lines contains the word "AYE" if there exists a pair of cities in the river network which is connected by a path of cost xi, or the word "NAY" otherwise. 

Output for each instance must be followed by a single line containing just the dot character. 

Sample Input

6
2 5 3 7 4 1 0
0
5 2 6 3 0
0
0
0
1
8
13
14
0
0

Sample Output

AYE
AYE
NAY
AYE
.

題目意思:

每次給你一個x,讓你判斷是否有兩點之間的路徑長度為x。

思路:

POJ - 1741差不多,只需要對計算的步驟稍作改動就好了。

註釋寫程式碼裡了:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 2 * 1e4 + 100;

int n, k, top, Max, root, ans, num;
int head[maxn], dist[maxn], size[maxn];
bool vis[maxn];

struct node {
	int v, w, next;
}edge[maxn];

inline void add(int u, int v, int w) {
	edge[top].v = v;
	edge[top].w = w;
	edge[top].next = head[u]; 
	head[u] = top++;
}

inline void Init() {
	top = 0;
	memset(head, -1, sizeof(head));
}

void dfs_size(int u, int father) {        //求以u為根的子樹的節點數量資訊
	size[u] = 1;        //先算上自己
	for(int i = head[u]; i != -1; i = edge[i].next) {
		int v = edge[i].v;
		if(v != father && !vis[v]) {
			dfs_size(v, u);
			size[u] += size[v];      //加上所有子節點的節點數量
		}
	}
}

void dfs_root(int r, int u, int father) {       //求子樹的重心
	int maxs = size[r] - size[u];              
	for(int i = head[u]; i != -1; i = edge[i].next) {
		int v = edge[i].v;
		if(v != father && !vis[v]) {
			dfs_root(r, v, u);
			maxs = max(maxs, size[v]);       //求最大子樹的節點數量
		}
	}
	if(maxs < Max) {             //通過比較最終得出重心
		Max = maxs;
		root = u;
	}
}

void dfs_distance(int u, int father, int dis) {     //求出以u為根的子樹所有節點到u的距離
	dist[num++] = dis;    //都存放在dist數組裡
	for(int i = head[u]; i != -1; i = edge[i].next) {
		int v = edge[i].v;
		if(v != father && !vis[v]) {
			dfs_distance(v, u, dis + edge[i].w);
		}
	}
}

int calc(int u, int dis) {    //計算每顆子樹的結果
	int res = 0;
	num = 0;
	dfs_distance(u, 0, dis);
	sort(dist, dist + num);
	int i = 0, j = num - 1;        //採用雙指標的方式來線性解決問題
	while(i < j) {
		if(dist[i] + dist[j] > k)
			j --;
		else if(dist[i] + dist[j] < k)
			i++;
		else {
			if(dist[i] == dist[j]) {        //如果兩個指標所指向的數字都相同了,那麼兩者之間所有的數都相同,從(j - i + 1)中挑兩個,求一下C(j - i + 1, 2)就行了
				res += (j - i + 1) * (j - i) / 2;
				break;
			}
			int l = i, r = j;
			while(dist[l] == dist[i]) ++ l;
			while(dist[r] == dist[j]) -- r;
			res += (l - i) * (j - r);
			i = l;
			j = r;
		}
	}
	return res;
}

void divide(int u) {     //分治
	Max = n;
	dfs_size(u, 0);
	dfs_root(u, u, 0);    //找到重心
	ans += calc(root, 0);        //計算該子樹的結果
	vis[root] = true;              //打上標記
	for(int i = head[root]; i != -1; i = edge[i].next) {
		int v = edge[i].v;
		if(!vis[v]) {
			ans -= calc(v, edge[i].w);      //減去不合法的情況
			divide(v);              //繼續遞迴求解
		}
	}
}

int main() {
	//freopen("in.txt", "r", stdin);
	while(cin >> n)
	{
		if(n == 0) break;
		int v, w;
		Init();
		for(int u = 1; u <= n; ++ u) {
			while(~scanf("%d", &v)) {
				if(v == 0) break;
				scanf("%d", &w);
				add(u, v, w);
				add(v, u, w);
			}
		}
		while(~scanf("%d", &k) && k) {
			ans = 0;
			memset(vis, false, sizeof(vis));   //記得每次初始化
			divide(1);
			if(ans > 0) {
				printf("AYE\n");
			}
			else {
				printf("NAY\n");
			}
		}
		printf(".\n");
	}
	return 0;
}