题目
解说
抱歉有点事要忙只能引用了……
这道题正解是用二分图,但是做完这道题的第二题才讲……所以我采用的我当时会的做法:并查集。
我们每有一个武器(a, b)时我们可以把它当做一条边(a, b)。
然后对于构图之后,一个大小为k联通块,我们发现有如下性质:
——如果这个联通块没有环(树):因为有k-1条边,那么总有方法使其中的k-1个节点被覆盖。容易知道这个联通块中一定不选最大的点。
——如果这个联通块有环:那么这k个节点一定都可以被覆盖
这时我们可以用并查集去维护联通块有没有环,联通块的大小。
然后我们扫一遍就行了……看看该联通块有没有环,如果没有环的话是不是看它是不是这个联通块的最大一个点。
引自https://www.luogu.com.cn/blog/user1923/solution-p1640
代码
(注释我写的)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000+3;//注意开n的范围而不是开数的范围
//因为攻击n次所以数最大只能用到n,后面比n大的都不用管
int fa[maxn],size[maxn];
bool cir[maxn];
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n+1;i++) fa[i]=i;
for(int i=1;i<=n+1;i++) size[i]=1;//至今不太明白为啥这两行只初始化到n会WA,洛谷不给数据……
for(int i=1;i<=n+1;i++){
int a,b;
cin>>a>>b;
int faa=find(a),fab=find(b);
if(faa==fab) cir[faa]=1;//已经在一个集合里说明有环
else{//合并
cir[faa]=cir[faa]|cir[fab];
cir[fab]=0;
size[faa]+=size[fab];
size[fab]=0;
fa[fab]=faa;
}
}
int ans;
for(int i=1;i<=n+1;i++){
int fai=find(i);
if (!cir[fai]){
if (size[fai]==1){
ans=i-1;
break;
}
else size[fai]--;
}
}
cout<<ans;
return 0;
}
幸甚至哉,歌以咏志。