bzoj1316树上的询问 树的点分治+STL-set

Posted GXZlegend

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1316树上的询问 树的点分治+STL-set相关的知识,希望对你有一定的参考价值。

题目描述

一棵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的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ1316]树上的询问 点分治

[bzoj1316]树上的询问_点分治

BZOJ_1316_树上的询问_点分治

BZOJ1316树上的询问 点分治+set

[POJ 1316] 树上的询问

BZOJ 4012 HNOI2015 开店 树的边分治