题目:HYSBZ - 1854
lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。 现在lxhgww想知道他最多能连续攻击boss多少次?
分析:
怎么又是游戏。。。SCOI的出题人是有多喜欢游戏。。。
咳,回到正题,不得不说这是一道较神的题,不是说它题有多厉害,而是黄学长的的解法很神,居然是并查集(以我这种蒟蒻的残缺脑回路是绝对想不出的。。。。)
首先我们将每一个属性值看做一个点,然后把每一把武器看做一条连接两点的边。
我们将所有的边连起来后可以得到的是一个有若干个树或者环的图。
对于每一个大小为n的一棵树来说,我们只能选择其中n-1个属性值,按照题意,应该优先选择编号较小n-1个点;
对于每一个大小为n的环来说,我们可以全部选择n个属性值。
则我们可以用并查集来实现:每有一条边,我们就合并两端点a,b,这里我们定义对于一个并查集树,除了它的根是还没有选择的,其他子节点都选过了,则有两种情况:
1.若a的根x不等于b的根y,则说明我们x和y这两个属性值从来没有选择过,按照优先选择小(假设是x)的来说,我们将x的打上选择标记,并将x并到y的上去,当然如果这里的x已经有标记了,就把y打上标记就行了。
2.若a的根x等于b的根y,则说明x,y中较小的已经有选择标记,则我就只需将较大的打上标记就行了。
最后枚举标记,看在哪个地方断开,答案就是多少。
没想到黄学长的代码还有错。。。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
using namespace std;
int fa[1000001],n,a,b,vis[1000001];
int read()
{
char ch=getchar();
int k=1,res=0;
while(!isdigit(ch)){if(ch=='-') k=-1;ch=getchar();}
while(isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
return res*k;
}
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=10001;i++) fa[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%d %d",&a,&b);
if(a>b) swap(a,b);
a=find(a),b=find(b);
if(a==b) vis[b]=1;
else
{
fa[a]=b;
if(!vis[a]) vis[a]=1;
else vis[b]=1;
}
}
for(int i=1;i<=n+1;i++)
if(!vis[i]) {
printf("%d",i-1);
break;
}
return 0;
}