牛客-小w的魔术扑克【并查集】

正题

题目链接:https://ac.nowcoder.com/acm/contest/1100/C


题目大意

\(n\)个数字\(m\)张扑克牌,每张两面有各有一个数字,可以选择一些扑克牌使用正面的数字,一些使用反面的,\(q\)次询问能否凑出\(l\sim r\)

\(1\leq n,m,q\leq 10^5\)


解题思路

每张牌看成连接两个点的一条边,那么显然如果一个大小为\(k\)的连通块,有\(k\)条或以上的边那么肯定能抽出整个联通块。

而如果是\(k-1\)条边,那么这棵树最多有一个数字无法被抽出,所以包括整棵树的区间不合法,并查集处理出这些区间判断即可。

时间复杂度:\(O(n\alpha (n))\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,fa[N],w[N],siz[N],l[N],r[N],mx[N];
int find(int x)
{return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		x=find(x),y=find(y);
		if(x==y){w[x]++;continue;}
		fa[x]=y;w[y]+=w[x]+1;
		siz[y]+=siz[x];
	}
	memset(l,0x3f,sizeof(l));
	for(int i=1;i<=n;i++){
		int x=find(i);
		l[x]=min(l[x],i);
		r[x]=max(r[x],i);
	}
	for(int i=1;i<=n;i++){
		if(find(i)==i){
			if(w[i]!=siz[i]-1)continue;
			mx[r[i]]=max(mx[r[i]],l[i]);
		}
	}
	for(int i=1;i<=n;i++)
		mx[i]=max(mx[i-1],mx[i]);
	scanf("%d",&m);
	while(m--){
		int l,r;
		scanf("%d%d",&l,&r);
		if(l>mx[r])puts("Yes");
		else puts("No");
	}
	return 0;
}
posted @ 2021-09-28 14:19  QuantAsk  阅读(66)  评论(0编辑  收藏  举报