SCOI2010(HYSBZ1854)“游戏”

来源:互联网 发布:淘宝设置主营类目 编辑:程序博客网 时间:2024/05/16 10:35

题目: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;}