【牛客CSP-S提高组赛前集训营1】C - 小w的魔术扑克【并查集】

题目大意:

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

小w喜欢打牌,某天小w与dogenya在一起玩扑克牌,这种扑克牌的面值都在1到n,原本扑克牌只有一面,而小w手中的扑克牌是双面的魔术扑克(正反两面均有数字,可以随时进行切换),小w这个人就准备用它来出老千作弊。小w想要打出一些顺子,我们定义打出一个l到r的顺子需要面值为从l到r的卡牌各一张。小w想问问你,他能否利用手中的魔术卡牌打出这些顺子呢?


思路:

巧妙。
想象如果一张牌的两个数字分别为 x , y x,y x,y,我们将 x x x y y y连边,那么最终就会形成几堆连通块。
如果一个连通块是一棵树,那么由于每一条边都可以选一个数字,那么显然不可以把这个连通块里的所有数字都选择上。
所以我们记录每一个连通块的边数、最大值和最小值,如果 m a x − m i n ≥ s i z e max-min\geq size maxminsize,那么久不可能构成一个 m i n ∼ m a x min\sim max minmax的顺子,也就是所有包含 [ m i n , m a x ] [min,max] [min,max]的询问都不可以选择成功。
那么将每一棵树按照最小值排序,设 l a s t [ i ] last[i] last[i]表示从 i i i开始最多可以接到多少的顺子,那么从 m m m枚举到1,在每一个 m i n ≥ i min\geq i mini的区间内取最小的 m a x max max,那么 l a s t [ i ] = m a x − 1 last[i]=max-1 last[i]=max1
然后就可以每次询问 O ( 1 ) O(1) O(1)回答了。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=100010;
int m,n,sum,pos,l,r,father[N],maxn[N],minn[N],size[N],last[N];

struct node
{
	int l,r;
}q[N];

bool cmp(node x,node y)
{
	return x.l<y.l;
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

int main()
{
	//freopen("ex.in","r",stdin);
	//freopen("ans.txt","w",stdout);
	scanf("%d%d",&m,&n);
	for (int i=1;i<=m;i++)
		father[i]=maxn[i]=minn[i]=i;
	for (int i=1,x,y;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		x=find(x); y=find(y);
		if (x!=y)
		{
			maxn[x]=max(maxn[x],maxn[y]);
			minn[x]=min(minn[x],minn[y]);
			size[x]+=size[y]+1;
			father[y]=x;
		}
		else size[x]++;
	}
	for (int i=1;i<=m;i++)
		if (father[i]==i && size[i]<=maxn[i]-minn[i])
			q[++sum].l=minn[i],q[sum].r=maxn[i];
	sort(q+1,q+1+sum,cmp);
	pos=m;
	for (int i=m;i>=1;i--)
	{
		for (;q[sum].l==i;sum--)
			pos=min(pos,q[sum].r-1);
		last[i]=pos;
	}
	scanf("%d",&m);
	while (m--)
	{
		scanf("%d%d",&l,&r);
		if (last[l]>=r) printf("Yes\n");
			else printf("No\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值