1. 程式人生 > >【bzoj1316】樹上的詢問 樹的點分治+STL-set

【bzoj1316】樹上的詢問 樹的點分治+STL-set

stl 有根樹 cnblogs esp soft 題目 != font 離線

題目描述

一棵n個點的帶權有根樹,有p個詢問,每次詢問樹中是否存在一條長度為Len的路徑,如果是,輸出Yes否輸出No.

輸入

第一行兩個整數n, p分別表示點的個數和詢問的個數. 接下來n-1行每行三個數x, y, c,表示有一條樹邊x→y,長度為c. 接下來p行每行一個數Len,表示詢問樹中是否存在一條長度為Len的路徑.

輸出

輸出有p行,Yes或No.

樣例輸入

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

樣例輸出

Yes
Yes
No
Yes


題解

樹的點分治+STL-set

由於有多組詢問,所以可以考慮把詢問離線,然後一起處理(貌似暴力也能過)。

然後就是點分治的經典題目了。

每次找子樹是查詢以前的某深度與現在的某深度形成的路徑長度是否為l。這個可以使用set維護。

時間復雜度$O(nq\log^2n)=O(能過)$。

#include <set>
#include <cstdio>
#define N 10010
using namespace std;
set<int> s;
int m , a[N] , head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , si[N] , mx[N] , sum , root , deep[N] , vis[N] , d[N] , tot;
bool ans[N];
void add(int x , int y , int z)
{
	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
void getroot(int x , int fa)
{
	int i;
	si[x] = 1 , mx[x] = 0;
	for(i = head[x] ; i ; i = next[i])
		if(!vis[to[i]] && to[i] != fa)
			getroot(to[i] , x) , si[x] += si[to[i]] , mx[x] = max(mx[x] , si[to[i]]);
	mx[x] = max(mx[x] , sum - si[x]);
	if(mx[x] < mx[root]) root = x;
}
void dfs(int x , int fa)
{
	int i;
	si[x] = 1 , d[++tot] = deep[x];
	for(i = head[x] ; i ; i = next[i])
		if(!vis[to[i]] && to[i] != fa)
			deep[to[i]] = deep[x] + len[i] , dfs(to[i] , x) , si[x] += si[to[i]];
}
void solve(int x)
{
	int i , j , k;
	vis[x] = 1 , s.clear() , s.insert(0);
	for(i = head[x] ; i ; i = next[i])
	{
		if(!vis[to[i]])
		{
			tot = 0 , deep[to[i]] = len[i] , dfs(to[i] , 0);
			for(j = 1 ; j <= tot ; j ++ )
				for(k = 1 ; k <= m ; k ++ )
					if(s.find(a[k] - d[j]) != s.end())
						ans[k] = 1;
			for(j = 1 ; j <= tot ; j ++ ) s.insert(d[j]);
		}
	}
	for(i = head[x] ; i ; i = next[i])
		if(!vis[to[i]])
			sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , solve(root);
}
int main()
{
	int n , i , x , y , z;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i < n ; i ++ ) scanf("%d%d%d" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d" , &a[i]);
		if(!a[i]) ans[i] = 1;
	}
	mx[0] = 1 << 30 , sum = n , getroot(1 , 0) , solve(root);
	for(i = 1 ; i <= m ; i ++ ) printf("%s\n" , ans[i] ? "Yes" : "No");
	return 0;
}

【bzoj1316】樹上的詢問 樹的點分治+STL-set