zoukankan      html  css  js  c++  java
  • [POI2008]MAF-Mafia

    题目

    大概理解一下这个图是(n)个点(n)条边的有向图,也就是一个基环内向树森林

    考虑一下一个大小为(S)的简单环怎么做

    画画图就知道,随便找个点顺着打过去,最少可以让(left lceil frac{S}{2} ight ceil)个人死;在一个点死之前让它去开一枪,最多可以让(S-1)个人死

    再来考虑一下套在环上的树

    首先这些树上有一些入度为(0)的节点,显然这些节点不可能被打死,于是考虑先这些点

    最小化死亡人数,考虑到这些入度为(0)的点一定要打死人,不如先打死那些被入度为(0)的点瞄准的人,这样这些人就不能开枪了。所以我们直接按照拓扑序开枪就好了。具体做法就是让一个活着的人去开枪,之后删掉被打死的人的出边,如果产生入度为(0)的点那么就说明这个点能活下来,就把他加进队列。

    这样我们就会搞到环上去,发现一旦我们打死了一个环上的人,那么这个环就会被破坏,按照上面的做法我们就能把这个环处理完。如果一个环的所有点都没有被其子树内的点打死,那么我们就利用上面的结论,环上的死亡人数就是(left lceil frac{S}{2} ight ceil)

    最大化死亡人数相对好做一下,我们发现对于一棵树来说只有入度为(0)的点才能活,这样我们就能把状态推到环上去,如果这个环上一旦有一个点被子树里的点射死(其实就是有子树),那么这个环上所有点都能被死。否则死亡人数就是(S-1)

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const int maxn=1e6+5;
    int n,tot,q[maxn],ans;
    int atk[maxn],in[maxn],vis[maxn],d[maxn];
    inline void del(int x) {in[atk[x]]--;if(!in[atk[x]]) q[++tot]=atk[x];}
    int find(int x) {
    	if(vis[x]) return 0;
    	vis[x]=1;
    	return find(atk[x])+1; 
    }
    int main() {
    	n=read();
    	for(re int i=1;i<=n;i++) atk[i]=read(),in[atk[i]]++;
    	for(re int i=1;i<=n;i++) if(!in[i]) q[++tot]=i;
    	for(re int i=1;i<=tot;i++) {
    		int x=q[i];
    		if(d[atk[x]]) continue;
    		d[atk[x]]=1;
    		del(atk[x]);
    	}
    	for(re int i=1;i<=n;i++) ans+=(d[i]==1);
    	for(re int i=1;i<=n;i++) 
    	if(in[i]&&!d[i]&&!vis[i]) {
    		int now=find(i);
    		ans+=(now==1?1:ceil((double)now/2));
    	}
    	printf("%d ",ans);
    	memset(in,0,sizeof(in));memset(d,0,sizeof(d));
    	memset(vis,0,sizeof(vis));ans=0;tot=0;
    	for(re int i=1;i<=n;i++) in[atk[i]]++;
    	for(re int i=1;i<=n;i++) if(!in[i]) q[++ans]=i;tot=ans;
    	for(re int i=1;i<=tot;i++) {
    		int x=q[i];
    		if(d[atk[x]]) continue;
    		d[atk[x]]=1;q[++tot]=atk[x];
    	}
    	for(re int i=1;i<=n;i++)
    	if(!d[i]&&in[i]&&!vis[i]) {
    		int now=find(i);
    		ans+=(now==1?0:1);
    	}
    	printf("%d
    ",n-ans);
    	return 0;
    }
    
  • 相关阅读:
    仿百度排列图片预览插件-Simple Lightbox
    vue2.0移动端自定义性别选择提示框
    微信小程序踩坑记
    网页里如何使用js禁用F12事件
    使用 html2canvas 实现浏览器截图
    h5、jq 移动端评论点攒功能
    js 数字递增特效 仿支付宝我的财富 HTML5
    aos.js超赞页面滚动元素动画jQuery动画库
    简洁AngularJS框架后台管理系统bootstrap后台模板
    Ionic 的 ng-class 在聊天功能上面的巧妙运用
  • 原文地址:https://www.cnblogs.com/asuldb/p/11536100.html
Copyright © 2011-2022 走看看