题解 P1640 【[SCOI2010]连续攻击游戏】
据说写写题解有益RP++
首先膜黄学长orz
这题可以用并查集做…… 新姿势get
我们每有一个武器(a, b)时我们可以把它当做一条边(a, b)。
然后对于构图之后,一个大小为k联通块,我们发现有如下性质:
——如果这个联通块没有环(树):因为有k-1条边,那么总有方法使其中的k-1个节点被覆盖。容易知道这个联通块中一定不选最大的点。
——如果这个联通块有环:那么这k个节点一定都可以被覆盖
这时我们可以用并查集去维护联通块有没有环,联通块的大小。
然后我们扫一遍就行了……看看该联通块有没有环,如果没有环的话是不是看它是不是这个联通块的最大一个点。
于是这是代码
#include<iostream>
#include<algorithm>
using namespace std;
int fa[1100000], cir[1100000], num[1100000];
int ans;
int n;
int gf(int t)
{
if (fa[t]==t) return t;
else
{
fa[t]=gf(fa[t]);
return fa[t];
}
}
int main()
{
ios::sync_with_stdio(0);
cin>>n;
for (int i=1; i<=n+1; ++i) fa[i]=i;
for (int i=1; i<=n+1; ++i) num[i]=1;
for (int i=1; i<=n; ++i)
{
int a, b;
cin>>a>>b;
int ap=gf(a), bp=gf(b);
if (ap==bp) cir[ap]=1;
else
{
cir[ap]=cir[ap]|cir[bp]; cir[bp]=0;
num[ap]+=num[bp]; num[bp]=0;
fa[bp]=ap;
}
}
for (int i=1; i<=n+1; ++i)
if (!cir[gf(i)])
{
if (num[gf(i)]==1) {ans=i-1; break;}
else num[gf(i)]--;
}
cout<<ans;
}