1. 程式人生 > >[POI2008]槍戰Maf題解

[POI2008]槍戰Maf題解

main spl display 最小和 子節點 取整 如何 view 標記

   

問題 C: [POI2008]槍戰Maf

時間限制: 1 Sec 內存限制: 256 MB

題目描述

有n個人,每個人手裏有一把手槍。一開始所有人都選定一個人瞄準(有可能瞄準自己)。然後他們按某個順序開槍,且任意時刻只有一個人開槍。因此,對於不同的開槍順序,最後死的人也不同。

輸入

輸入n人數<1000000 每個人的aim

輸出

你要求最後死亡數目的最小和最大可能

樣例輸入

8
2 3 2 2 6 7 8 5

樣例輸出

3 5

  本次考試最後一個題,被老師評論稱難炸了……然而貌似不那麽難,但你絕對打不A。

  這道題只能怪我代碼能力太弱,該想到的都想到了,然而還是華麗麗的爆了0。

  首先讓我們先明確幾條性質來幫助我們做題:

    1. 對於每個圖,他至少有一個環或者一個人自殺。縮完點之後它是樹或樹林。證明:假設我們已經輸入了n個人,先忽略一下第n個人要殺誰,如果之前沒有人自殺或環的話它一定是一棵樹,因為有n個點和n-1條邊,那麽第n個人指向哪裏就很關鍵了,他指向每一個其他人都會形成一個環,而指向他自己又是自殺,所以,第一條get。
    2.   每一個沒人殺的人都會存活,每一個自殺的人最終都會死。不解釋。
    3. 每個人對於答案的影響不在於他被誰殺掉,而在於他殺掉誰,世界不關心你說了什麽,世界只關心你做了什麽。
    4. 一個環如果沒人幹預,那麽它最少死n/2個人,向下取整。也就是一個人如果不殺人就只能被殺死,對於奇數個點的環你殺了人也可能被殺死。最多就是總人數-1。
    5. 一個環如果有人幹預,那麽它最多就是全部被殺,最小在下面說。
    6. 如果一個人沒死,那麽他指向的那個人就一定會死,如果指向那個人的人都死了,那麽他就可以活下來,這一點在環中同樣適用。

  為了方便,我們直接去求活著的人數,反正人不是活著就是死了廢話。

  先說死的人最少,那麽入度為0的人一定活下來了,因此我們用隊列慢慢往上爬即可,只要隊列中的人他所殺的人要殺的人沒人殺他了,那麽他也可以入隊。至於沒人指向的環嘛,上面說了。

  死的最多的人就是讓人們從樹頂從上往下開槍,直到葉子節點所以答案就為縮完點後入度為0的點。

  我打完之後只過了一個點,因為這題有兩個坑點,至少對我來說是這樣的。

  第一,指向這個點的點可以有好幾個,但這個點的出度只有一個,雖然是廢話,但在你判斷誰存活的時候需要先判斷一下,你目前隊首的這個點所指向的點是否已經被殺掉,否則錯炸了。

  第二,也是卡住了無數英雄好漢的點,如何判斷某個環是否已被用隊列訪問,開個bool記錄看似容易,然而在哪裏打標記就是大坑了,對於WA的童鞋們可以試一下這個點:

      4

      2 3 2 3

  希望能幫到你們,在這個點中,環中每個人都死了,但如果你在將某個元素塞進隊列時才將它打上標記你會發現實際已經訪問了的環並未被打上標記,因此,正解是每個被塞進隊列的人他所指向的人和他所指向的人所指向的人都要被打上標記。

  

技術分享
  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<queue>
  6 #include<algorithm>
  7 #include<cmath>
  8 using namespace std;
  9 int n,tt[1000004],st[1000005],top;
 10 bool rz[1000005],rz2[1000005];
 11 int dfn[1000005],low[1000005],zz1;
 12 int zz2,belong[1000005],sum[1000005];
 13 void tar(int x){
 14     zz1++;
 15     top++;
 16     st[top]=x;
 17     rz[x]=rz2[x]=1;
 18     dfn[x]=low[x]=zz1;
 19     if(!rz2[tt[x]])
 20     {
 21         tar(tt[x]);
 22         low[x]=min(low[x],low[tt[x]]);
 23     }
 24     else if(rz[tt[x]])
 25     {
 26         low[x]=min(low[x],dfn[tt[x]]);
 27     }
 28     if(dfn[x]==low[x])
 29     {
 30         int v;
 31         zz2++;
 32         do{
 33             v=st[top];
 34             top--;
 35             rz[v]=0;
 36             sum[zz2]++;
 37             belong[v]=zz2;
 38         }while(dfn[v]!=low[v]);
 39     }
 40 }
 41 int a[1000005],zz3,rd2[1000006],be[1000005];
 42 bool fw[1000005],js[1000005];
 43 int rd[1000005],ans1,ans2;
 44 queue<int> q1;
 45 int main(){
 46     scanf("%d",&n);
 47     for(int i=1;i<=n;i++)
 48     {
 49         scanf("%d",&tt[i]);
 50         rd[tt[i]]++;
 51     }
 52     for(int i=1;i<=n;i++)
 53         if(!rz2[i])
 54             tar(i);
 55     for(int i=1;i<=n;i++)
 56     {
 57         if(rd[i]==0)
 58         {
 59             ans1++;
 60             q1.push(i);
 61             fw[belong[i]]=1;
 62         }
 63     }
 64     while(!q1.empty())
 65     {
 66         int x=q1.front();
 67         q1.pop();
 68         fw[belong[tt[x]]]=1;
 69         if(!js[tt[x]])
 70         {
 71             js[tt[x]]=1;
 72             rd[tt[tt[x]]]--;
 73             if(rd[tt[tt[x]]]==0)
 74             {   
 75                 ans1++;
 76                 fw[belong[tt[tt[x]]]]=1;
 77                 q1.push(tt[tt[x]]);
 78             }
 79         }
 80     }
 81     for(int i=1;i<=zz2;i++)
 82     {
 83         if(!fw[i])
 84         {
 85             ans1+=sum[i]/2;
 86         }
 87     }
 88     for(int i=1;i<=n;i++)
 89     {
 90         if(belong[tt[i]]!=belong[i]||tt[i]==i)
 91         {
 92             rd2[belong[tt[i]]]++;
 93         }
 94     }
 95     for(int i=1;i<=zz2;i++)
 96     {
 97         if(!rd2[i])
 98             ans2++;
 99     }
100     printf("%d %d",n-ans1,n-ans2);
101     return 0;
102 }
103  
View Code

對了,這道題打法不知一道,有興趣的讀者可以試一試別的方法,DP已被驗證可行,貪心據說也可以,希望讀者不要拘泥於一種打法。

[POI2008]槍戰Maf題解