CF1328E Tree Queries

CF1328E Tree Queries

应该还是比较妙的

题意

给你一个树,然后多次询问

每次询问给出一堆节点,问你是否能找到一个从根出发的链,是的对于给出的每个节点,都能找出链上的点,是的他们的距离小于等于\(1\)

\(n\leq 2\cdot 10^5,m\leq 2\cdot 10^5,\sum k\leq 2\cdot 10^5\)

其中\(m\)是询问次数,\(k\)是每次给出的点数



首先,一个点要想符合题目的条件,无非有两种情况

一种是就在链上,这个好说

另一种是距离链上的点距离为\(1\),那么,肯定是他的父亲在链上

所以,我们可以把每个给出的点转换成他的父亲,问题就变成看:能否找到一个链,是的这些点都在链上

这样的转换对第一种肯定是没有影响的,一个点在链上,他的父亲肯定也在链上

所以考虑转换后的问题

我首先想到的是,找出深度最大的节点,对于其它每个点和它求 LCA ,如果这个 LCA 不是那个深度较小的点,说明肯定连不成一个链

这一点判断基于那个链必须从根出发

然后写完代码交上去:

#100 WA!

emmm,估计是写炸了,第一个MLE是邻接表忘开2倍了

然而并没看出错,所以考虑一种实现更简单的方法

上面求 LCA 其实可以理解为从下向上考虑,那么还有一种从上往下考虑的方法:

先把每个点按深度从小到大排序,然后如果p[i+1]不在p[i]的子树里,那么肯定连不成一个链

至于怎么判断也很简单,记录一个dfs序和每个节点子树大小就行,具体见代码的check函数

\(\texttt{code.}\)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<‘0‘||c>‘9‘){if(c==‘-‘) y=0;c=std::getchar();}
	while(c>=‘0‘&&c<=‘9‘){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,m;
int fir[200006],nex[400006],to[400006],tot;
int p[200006],fa[200006],size[200006];
int dfn[200006],deep[200006],dfscnt;
inline void add(int a,int b){
	to[++tot]=b;
	nex[tot]=fir[a];fir[a]=tot;
}
void dfs(int u,int fat){
	fa[u]=fat;deep[u]=deep[fat]+1;dfn[u]=++dfscnt;size[u]=1;
	for(reg int i=fir[u];i;i=nex[i])if(to[i]!=fat) dfs(to[i],u),size[u]+=size[to[i]];
}
inline int check(int a,int b){//看b是不是在a的子树
	if(dfn[b]<dfn[a]) return 0;
	if(dfn[b]>=dfn[a]+size[a]) return 0;
	//这里一定是>=,如果dfn[b]=dfn[a]+size[a],也说明b并不在a的子树里,因为统计size[a]的时候a这个点也被算进去了
	return 1;
}
inline int cmp(int x,int y){return deep[x]<deep[y];}
int main(){
	n=read();m=read();
	for(reg int a,b,i=1;i<n;i++){
		a=read();b=read();
		add(a,b);add(b,a);
	}
	reg int k;
	dfs(1,1);
	while(m--){
		k=read();
		for(reg int i=1;i<=k;i++) p[i]=fa[read()];
		std::sort(p+1,p+1+k,cmp);
		for(reg int i=2;i<=k;i++)
			if(!check(p[i-1],p[i])) goto NO;
		std::puts("YES");continue;
		NO:;std::puts("NO");
	}
	return 0;
}

那个没A的LCA做法也放在这,也许哪天很闲的时候会再调调

记录

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<‘0‘||c>‘9‘){if(c==‘-‘) y=0;c=std::getchar();}
	while(c>=‘0‘&&c<=‘9‘){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,m;
int fir[200006],nex[400006],to[400006],tot;
int p[200006];
int fa[23][200006],deep[200006];
inline void add(int a,int b){
	to[++tot]=b;
	nex[tot]=fir[a];fir[a]=tot;
}
void dfs(int u,int fat){
	fa[0][u]=fat;deep[u]=deep[fat]+1;
	for(reg int i=fir[u];i;i=nex[i])if(to[i]!=fat) dfs(to[i],u);
}
inline int check(int a,int b){
	for(reg int i=20;~i;i--)
		if(deep[fa[i][b]]>=deep[a]) b=fa[i][b];
	return a==b;
}
int main(){
	n=read();m=read();
	for(reg int a,b,i=1;i<n;i++){
		a=read();b=read();
		add(a,b);add(b,a);
	}
	reg int k,maxdeep,maxdeepid;
	dfs(1,1);
	for(reg int i=1;i<=n;i++)
		for(reg int j=1;j<=20;j++) fa[j][i]=fa[j-1][fa[j-1][i]];
	while(m--){
		k=read();maxdeep=0;
		for(reg int i=1;i<=k;i++){
			p[i]=fa[0][read()];
			if(deep[p[i]]>maxdeep) maxdeep=deep[p[i]],maxdeepid=p[i];
		}
		for(reg int i=1;i<=k;i++)if(p[i]!=maxdeepid){
			if(!check(p[i],maxdeepid)) goto NO;
		}
		std::puts("YES");continue;
		NO:;std::puts("NO");
	}
	return 0;
}

原文地址:https://www.cnblogs.com/suxxsfe/p/12585217.html

时间: 2024-10-18 17:46:03

CF1328E Tree Queries的相关文章

[CF1328E] Tree Queries - LCA

给定一棵有根树,每次询问给定一个点集,问是否存在根到某点的链,使得点集中所有点到链的距离不大于 \(1\). Solution 将每次询问的结点按深度排序好,相邻的两个结点 \(p,q\) 一定满足 \(d[p]-d[lca] \le 1 \or d[q]-d[lca] \le 1\),其中 \(lca=lca(p,q)\) 必要性显然,充分性考虑让毛毛虫的茎一直往差值大的那边走即可 #include <bits/stdc++.h> using namespace std; #define i

AC日记——825G - Tree Queries

825G - Tree Queries 思路: 神题,路径拆成半链: 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 1000005 #define INF 0x3f3f3f3f int n,m,val[maxn],head[maxn],E[maxn<<1],V[m

Educational Codeforces Round 25 G. Tree Queries

题目链接:Educational Codeforces Round 25 G. Tree Queries 题意: 给你一棵树,一开始所有的点全是黑色,有两种操作. 1 x 将x这个点变为黑色,保证第一个操作是这个. 2 x 询问x到任意黑色的点的简单路径上的最小节点编号. 题解: 首先将一个变为黑色的点当成树根,然后dfs一下,预处理出所有点的答案. 然后开一个变量记录一下当前变黑的点的答案cur=min(cur,dp[x]). 每次询问的时候答案就是min(cur,dp[x]). 如果觉得很神

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

https://codeforces.com/contest/1328/problem/E E. Tree Queries You are given a rooted tree consisting of nn vertices numbered from 11 to nn. The root of the tree is a vertex number 11. A tree is a connected undirected graph with n−1n−1 edges. You are

CF-1328 E. Tree Queries

E. Tree Queries 题目链接 题意 给定一个树,每次询问一组点,问是否存在一条从根到某点的路径,使得该组点到该路径的最短距离不超过1 分析 从根到达某点的路径,如果覆盖到了某个点,那么一定会覆盖它的父亲(根除外),所以对组内的点替换成他们的父亲,问题转换为是否存在一条从根出发的路径覆盖所有的点.做法是将这些点按照深度从小到大排序,然后深度小的必须为深度大的的祖先 相邻两点求LCA即可,由于题目特殊性,前面的点和后面的点必须和根在一条直直的路径上,所以可以用欧拉序直接来判断是否可行 另

[SGU550]Tree Queries Online

题意:给一棵带边权的树,按给定的顺序删掉所有边,每删一条边,输出权值$w$,并把小的那块全部乘$w$,大的那块全部加$w$,强制在线 原题是sgu的,但现在sgu好像挂了,幸运地在codeforces gym里面找到这个题(题号是K) 本来这个并不是数据结构题,按题解的说法应该是用一些奇技淫巧转化一下,但为了好玩我还是写了个伪ETT练一下手 ETT全名是Euler Tour Tree,顾名思义就是用平衡树维护欧拉遍历序 对于这棵树,欧拉遍历序就是123242151,虽然它是一个环,但通常约定在根

CodeForces 825G&quot;Tree Queries&quot;(建树选根)

传送门 •参考资料 [1]:CodeForces 825G Educational Round #25 G :建树选根大法+O1大法+iostream解绑了还是慢 •题意 给定一颗包含 n 个节点的树,开始树的所有节点都是白色的: 给出 q 次询问,询问分为1.2两种: 将节点 x 涂成黑色. 询问节点 x 到所有的黑点节点的简单路径中的标号最小的那个点(包括起点和黑点) 题目保证第一次询问是 1 类型的. •题解 如果我们随便选取某节点作为根节点,那么询问的时候,我们要找到节点 x 到所有黑色

CF825G Tree Queries

[题意] 一棵树有 n个节点,初始均为白色,有两种操作: 1. 1 x 代表把结点 x 设置为黑色 2. 2 x 代表查询 x 到树上任意一个黑色结点的简单路径上的编号最小的结点的编号 输入 t 和 z ,其中 t 表示操作类型, x=(last+z)mod n+1,last代表上一次询问答案,初始为 0 ,保证第一个操作为 1 [数据范围] n,q<=1e6. [题解] 发现第一个操作保证是1,则可以以第一个染黑的点为根,则每个点到它路径上最小值dis[i]可以预处理出. 发现一个点到所有黑点

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

https://codeforces.com/contest/1328/problem/E 题目所描述的是一棵树,题中已明示1为root结点. 题目可以转化为,是否存在一条路径,满足集合中的k个点到路径的距离小于等于1? 思路: 1.首先倍增离线预处理出结点深度,便于后续在线询问LCA 2.对于每次的询问,依次扫描k个点.对于集合中的u和v两点,每次我们求出u和v的LCA,计算u和v到LCA的距离,如果u和v到LCA的距离同时大于1,那么说明无法找到一条路径,使得u和v到该路径链的距离小于等于1