暑假集训Day8 P3472 [POI2008]MAF-Mafia(思惟题)

题目大意

你猜呀c++

输入格式

你猜呀算法

输出格式

你猜呀安全

数据范围与提示

你接着猜呀spa

算法分析

  • 设最少存活人数为MIN 最多存货人数为MAX
    来看张图:指针

  • 首先咱们先来统计一下每一个点的入度,若是一个点的入度为0,则自始至终这我的都不可能被杀(没人想杀小姐姐),就像图里的1和4,因此这我的想杀的人必定会死(震惊) 也就是图中的2code

  • 每一个人都会有想杀的人(咱也不知道为啥会想杀本身),一我的若是入度不为0 ,他想杀的人就不必定会死(为了保护你想杀的小姐姐 先把你干掉)
    这样的点的目标就像3,可能会死掉(若是拯救小姐姐的人来晚了,小姐姐就被大魔王煮着吃了)
    咱们能够发现:blog

  • 若是一个点入度为0(绿框) 那么这个点必定会活下来 :MIN++ MAX++队列

  • 最小存活:让一定会死的人2先把本身要杀的人3杀掉(undie标记3)(在勇士救出小姐姐以前吃了她)则MIN就不能加1了get

  • 最大存活:先把一定会死的人杀死防止他去杀他要杀的人(勇士在魔王吃掉小姐姐以前干掉大魔王),那么最大存活就会加1了(就像例图中3被解救了)
    But 对小姐姐图谋不轨的人远不止一个呢? 所以咱们不能直接断定小姐姐是能够存活的,可是咱们能够将小姐姐的入度--
    若是入度成功变成了0。 恭喜你,小姐姐得救了(虽然不能和你幸福地生活在一块儿),咱们就能够将这个点入队了(队列维护入度为0的点,表示安全的点)it

  • 可是若是这个点的入度并无变成0 则这个点必定也会构成一个环或者链 ,关于链显然隔一我的打一我的能够存活最多的人(n/2),存活最少的人就是只活一个(很简单,本身推)

  • 若是环上有undie标记的人,可让这我的最后死,而后在这个环死的只剩他的时候,BOOM 干掉他

  • 固然还有一种状况就是直接的无任何undie标记的环 , 同上
    1.最小存活: +1
    2.最大存活: +n/2

  • 具体的细节去看注释吧

代码展现

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,Max,Min,q[maxn],aim[maxn],rd[maxn];
bool die[maxn],undie[maxn];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&aim[i]);
        rd[aim[i]]++;
    }
    for(int i=1;i<=n;++i)
        if(rd[i]==0){//入度为0的点进队 
            Min++;//最小存活++
            q[++Max]=i;//最大存活++
        }
    int head=1;//模拟指针
    while(head<=Max){
        int cur=q[head];head++;//队首出队
        if(die[aim[cur]]) continue;//若是队首要杀的人已经被别人杀了
        die[aim[cur]]=1;//标记队首要杀的人
        int live=aim[aim[cur]];//队首目标的目标可死可不死
        rd[live]--;//入度--
        undie[live]=1;//能够不死了
        if(rd[live]==0)//若是入度变为0了
            q[++Max]=live;//最大存活数++,入队
    }
//下面是处理环的
    for(int i=1;i<=n;++i)
    	if(rd[i] && !die[i]){//若是当前位置入度不为0 而且还没死
            int len=0,flag=0;//len为环的长度 flag断定当前人是否可能会死
            for(int j=i;!die[j];j=aim[j]){//遍历环
                len++;//环长度++
                flag|=undie[j];//当前人是否可能会死
                die[j]=1;//标记为已死 防止下次i到这个位置再计算
            }
            if(!flag && len>1) Min++;//若是当前点不可能会死 并且环长度>1(不是自环) ,最小存活数++
            Max+=len/2;//最大存活数加上环长度的一半
    } 
    printf("%d %d",n-Max,n-Min);//要输出最小死亡数和最多死亡数
    return 0;
}

感谢观看 点个关注>:<

相关文章
相关标签/搜索