暑假集訓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;
}

感謝觀看 點個關注>:<

相關文章
相關標籤/搜索