洛谷P2189 小Z的传感器

链接:P2189

题目描述
众所周知,小 Z 家是个豪宅,有 n 个房间,并通过 m 条通道相连(家当然是连通的)。

有一天,小 Y 想趁小 Z 不在偷偷光顾他家,并决定到他家的每个房间至少逛一次。不幸的是,小X 家有 k 个房间装了传感器,该传感器会在第一次有人到访的时候返回信息。

当小 Z 回到家时,就发现小 Y 来过了,小 Y 也如实地告诉了小 Z 自己到每个房间至少逛了一次。

然而,小 Z 仔细研究了传感器返回信息的先后顺序,怀疑个别传感器可能返回信息有延迟。

为了验证自己的推断,连同这一次在内,他一共让小 Y 到他家来了 q 次。他想判断每次传感器返回信息的先后顺序是否可能出现,希望你帮帮他。

输入格式
第一行包含四个整数 n,m,k,q。

接下来 m 行,每行包含两个整数 x,y,表示房间 x 和房间 y 有一条双向通道相连。

接下来 q 行,每行包含 k 个整数,表示每次按先后顺序返回信息的传感器所在房间的编号。

输出格式
q 行,每行包含一个字符串“Yes”或“No”,表示每次传感器返回信息的先后顺序是否可能出现。

输入输出样例
输入 #1
5 5 3 2
1 2
2 3
3 1
1 4
4 5
4 2 1
4 1 2
输出 #1
No
Yes

【数据规模】

对于 10% 的数据,n ≤ 2;

对于 30% 的数据,n ≤ 3;

对于 60% 的数据,n ≤ 10000,m ≤ 20000,k ≤ 10;

对于 100% 的数据,1 ≤ k ≤ n ≤ 10^5,1 ≤ m ≤ 2 × 10^5,1 ≤ q ≤ 5,x ≠ y。


又是一个连通问题,仍然选择并查集来解决
题目我们可能一开始看见有点懵,就是

判断每次传感器返回信息的先后顺序是否可能出现

这句话到底是什么意思?
其实不可能出现的原因只有一个
就是相邻两个传感器不会经过有其他传感器的房间
那么这个时候我们就要有请我们的并查集先生上线来解决这个问题
我们先把没有传感器的连起来,因为他们是不会影响结果的
然后我们再把有传感器那k个的按照他输入的顺序将每一个有传感器的与其有关的且没有传感器的(或者在这个传感器前面的有传感器的)每次合并一个,然后判断与前面的是否在一颗子树(一个集合),如果不相连就说明肯定中间有别的传感器否则不会通则输出No,如果k个之后都相邻的可以相连,则输出Yes即可。

代码:

#include <cstdio>
#include <iostream>
using namespace std;
struct CZP
{
	int next,to;
}a[1000001];
int h[100001],c[100001],n,m,k,q,fa[100001],b[100001],top;
void cun(int from,int to)
{
	a[++top].next=h[from];
	a[top].to=to;
	h[from]=top;
}  //邻接表存储
void cs()
{
	for (int i=1;i<=n;i++)
	fa[i]=i;
}  //初始化
int find(int x)
{
	if (fa[x]==x)
	return x;
	fa[x]=find(fa[x]);
	return fa[x];
}  //并查集路径压缩
void he(int j)
{
	int k=h[j];
	while (k!=-1)
	{
		if (b[a[k].to]==0)
		{
			int x1=find(j),y1=find(a[k].to);
		    if (x1!=y1)
		    	fa[y1]=x1;
		}
		k=a[k].next;
	}
}   //把与这个点相连的如果标记可以相连的就合并操作
int main()
{
	scanf("%d%d%d%d",&n,&m,&k,&q);
	for (int i=1;i<=n;i++)
	h[i]=-1;
	for (int i=1;i<=m;i++)
{
	int x,y;
	scanf("%d%d",&x,&y);
	cun(x,y);
	cun(y,x);
}
   for (int i=1;i<=q;i++)
   {
   	int kk=0;
   	 cs();
   	   for (int j=1;j<=k;j++)
   	   {
		 scanf("%d",&c[j]);
		 b[c[j]]=1;  //先设置所有的有传感器的不能合并
	   }
	  b[c[1]]=0;
	   for(int j=1;j<=n;j++)
	   {
	   	if (b[j]==0) //先把没有传感器的有传感器的第一个合并起来
	   	he(j);
	   }
	   for (int j=2;j<=k;j++)
	   {
	   	  b[c[j]]=0;  //然后再一个一个设置合并
	   	  he(c[j]);
	   	  if (find(c[j])!=find(c[j-1]))  //判断与相邻的前一个是否可以相连
	   	  {
	   	  	  printf("No\n");
	   	  	  kk=1;
	   	  	  break;
		}
	   }
	   if (!kk)
	   printf("Yes\n");
   }
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值