解题思路
如果一张牌的两个数字分别为 x , y x , y x,y,我们将x和y连边,那么最终就会形成几堆连通块。
如果一个连通块是一棵树,那么选每一条边相对于可以选一个数字。
所以我们记录每一个连通块的边数、最大值和最小值,如果
m
a
x
−
m
i
n
≥
s
i
z
e
max−min≥size
max−min≥size,那么久不可能构成一个
m
i
n
∼
m
a
x
min∼max
min∼max的顺子,也就是所有包含
[
m
i
n
,
m
a
x
]
[min,max]
[min,max]的询问都不可以选择成功。
统计下所以不合理的区间,将取件按照左端点排序,设 l a s t [ i ] last[i] last[i]表示从i开始最多可以接到多少的顺子,那么从m 枚举到1,在每一个min≥i的区间内取最小的max,那么 l a s t [ i ] = m a x − 1 last[i]=max−1 last[i]=max−1。
代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
int n,m,sum,T,x,y,fa[100010],minn[100010],maxn[100010],size[100010],last[100010];
struct c{
int l,r;
}a[100010];
int find(int x)
{
if(fa[x]==x)return fa[x];
else return find(fa[x]);
}
bool cmp(c l,c r)
{
return l.l<r.l;
}
int main()
{
// freopen("yy.in","r",stdin);
// freopen("yy.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
fa[i]=maxn[i]=minn[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
int xx=find(x),yy=find(y);
if(xx!=yy)
{
size[xx]+=size[yy]+1;
fa[yy]=xx;
minn[xx]=min(minn[xx],minn[yy]);
maxn[xx]=max(maxn[xx],maxn[yy]);
}
else size[xx]++;
}
for(int i=n;i>0;i--)
{
if(fa[i]==i&&size[i]<=maxn[i]-minn[i])
{
a[++sum].l=minn[i],a[sum].r=maxn[i];
}
}
sort(a+1,a+sum+1,cmp);
int t=n;
for(int i=n;i>0;i--)
{
for (;a[sum].l==i;sum--)
t=min(t,a[sum].r-1);
last[i]=t;
}
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&x,&y);
if(last[x]>=y)
printf("Yes\n");
else printf("No\n");
}
}