Codeforces Round #629 (Div. 3) E.Tree Queries

Source

题目大意

给定一颗根为 1 1 1 号点的树。有 m m m 次询问,第 i i i 次询问给定 K i K_i Ki 个不同的节点,问你是否能找到一条从根到任意节点 x x x 的树上路径,满足之前给定的 K i K_i Ki 个不同的节点要么在路径上,要么离路径的距离为 1 1 1,每次询问输出 Y E S YES YES or N O NO NO

解法:
先预处理出LCA的相关数组(包括每个点的深度 d e p t h [ ] depth[] depth[])。
每次询问,先将给定的 K i K_i Ki 个节点按照深度排序。因为题目要求从根节点到任意节点的路径,可知该路径上的深度严格递增,所以我们可以按照深度从小到大来从根节点依次访问 (本题中,距离 < = <= <= 1 1 1 即可 “访问” ) 给定的节点。

设当前已经到节点 a a a, 下一步我们按照深度顺序需要访问的节点为 b b b;
已知 d e p t h [ a ] < = d e p t h [ b ] depth[a] <= depth[b] depth[a]<=depth[b],

d i s ( x , y ) dis(x,y) dis(x,y) 为节点x到节点y的树上距离

① 若 d i s ( a , L C A ( a , b ) ) = = 0 dis(a,LCA(a,b) ) == 0 dis(a,LCA(a,b))==0 , 即 a = = L C A ( a , b ) a == LCA(a,b) a==LCA(a,b),则可以直接走到节点 b b b;
② 若 d i s ( a , L C A ( a , b ) ) = = 1 dis(a,LCA(a,b) ) == 1 dis(a,LCA(a,b))==1 , 则可以先到退回到节点 L C A ( a , b ) LCA(a,b) LCA(a,b) , 再到节点 b b b;
③ 若 d i s ( a , L C A ( a , b ) ) > 1 dis(a,LCA(a,b) ) > 1 dis(a,LCA(a,b))>1 , 则显然,不存在满足条件的路径。

因此,从 1 1 1 ~ K i K_i Ki 逐一判断即可。

CODE:

#include <bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int MX = 2e5 + 7;
const int mod = 1e9 + 7;

ll gcd(ll a,ll b){return b ? gcd(b,a%b):a;}
ll quimod(ll a,ll k){ll res = 1;while(k){if(k & 1) res = (res * a) % mod; a = (a * a) % mod;k >>= 1;}return res;}
int n,q;
int cnt,head[MX << 1];
int depth[MX << 1];
int fa[MX << 1][30];
struct Edge{
	int v,next;
}e[MX << 1];
void add(int u,int v){
	e[++cnt].v = v;e[cnt].next = head[u];head[u] = cnt;
	e[++cnt].v = u;e[cnt].next = head[v];head[v] = cnt;
}
void dfs(int u,int pre,int d){
	fa[u][0] = pre;depth[u] = d;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].v;
		if(v != pre) dfs(v,u,d + 1);
	}
}
void init(){
	memset(fa,-1,sizeof(fa));
	dfs(1,-1,0);
	for(int j = 0;((1 << (j + 1)) < n);++j){
		for(int i = 1;i <= n;++i){
			if(fa[i][j] >= 0) fa[i][j + 1] = fa[fa[i][j]][j];
		}
	}
}
int LCA(int u,int v){
	if(depth[u] > depth[v]) swap(u,v);
	int temp = depth[v] - depth[u];
	for(int i = 0;(1 << i) <= temp;++i){
		if((1 << i) & temp) v = fa[v][i];
	}
	if(v == u) return u;
	for(int i = log2(n);i >= 0;--i){
		if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
	}
	return fa[u][0];
}
int a[MX];
bool cmp(int x,int y){
	return depth[x] < depth[y];
}
int main()
{
	scanf("%d %d",&n,&q);
	for(int i = 1;i < n;++i){
		int u,v;scanf("%d %d",&u,&v);add(u,v);
	}
	init();
	while(q--){
		int m;scanf("%d",&m);
		for(int i = 1;i <= m;++i) scanf("%d",&a[i]);
		sort(a + 1,a + 1 + m,cmp);
		a[0] = 1;
		bool f = true;
		for(int i = 1;i <= m;++i){
			int dis = depth[a[i - 1]] - depth[LCA(a[i - 1],a[i])];
			if(dis > 1) {f = false;break;}
		}
		if(f) printf("YES\n");
		else printf("NO\n");
	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值