码迷,mamicode.com
首页 > 其他好文 > 详细

[POI2008]MAF-Mafia

时间:2019-09-17 19:19:31      阅读:90      评论:0      收藏:0      [点我收藏+]

标签:max   最小   double   class   define   std   产生   理解   char   

题目

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

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

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

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

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

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

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

最大化死亡人数相对好做一下,我们发现对于一棵树来说只有入度为\(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",n-ans);
    return 0;
}

[POI2008]MAF-Mafia

标签:max   最小   double   class   define   std   产生   理解   char   

原文地址:https://www.cnblogs.com/asuldb/p/11536100.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!