牛客-小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;
}