• [SCOI2010] 连续攻击游戏


    题目

    原题链接

    解说

    抱歉有点事要忙只能引用了……
    这道题正解是用二分图,但是做完这道题的第二题才讲……所以我采用的我当时会的做法:并查集。

    我们每有一个武器(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;
    } 
    

    幸甚至哉,歌以咏志。

  • 相关阅读:
    ul前面有40px的距离怎么办
    JQuey中 attr('checked', true)设置状态只有第一次有用
    只有一个RADIO的单选框如何在选中后取消选中
    为Table中的thead加上边框
    ADB 无线连接设备
    面试准备的内容
    蓝牙MESH相关代码
    怎样重构代码
    safari 调试iPhone web页面
    Appium1.6.4 真机运行ios10.3.1 填坑记
  • 原文地址:https://www.cnblogs.com/DarthVictor/p/12827457.html
Copyright © 2020-2023  润新知