枪战Maf (bzoj 1124)

Description

有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。

Input

输入n人数<1000000 每个人的aim

Output

你要求最后死亡数目的最小和最大可能

Sample Input

8
2 3 2 2 6 7 8 5

Sample Output

3 5

解析拓扑

 
设最大存活人数 mx,最少存活人数 mn
 
如图,入度为 0 的点【绿框】必定存活,那么他所指的点【红圈】必定会死(早晚的事)。所以每有一个“绿框”,我们就:mx++,mn++; //这,毫无疑问
 
我们可以发现:
  • 最小存活:如果 2 死之前射死了 3 (undie标记),那么 mn 不变
  • 最大存活:如果 2 先死了,3 的入度--,他的入度变为了 0 ,进队;当然 3 也有可能入读不为 0,那么它必定会构成环或链,想要尽量活的人多,那就要这个环或链隔一个人,打一个,最多就可以存活 len/2 个人

最常见的还是构成环的情况【黄圈】(无undie标记),同理,

  • 最小存活:+1(无论如何都会剩一个人)
  • 最大存活:隔一个人打一个,可以最大存活 len/2;
 
其实我们可以发现,拓扑序帮我们把一个大整体拆解成了一个个小问题
 
code
#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;
const int MX=1000001;
int n,mx,mn,q[MX],aim[MX],indu[MX];
bool die[MX],undie[MX];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) {
        scanf("%d",&aim[i]);
        indu[aim[i]]++;
    }
    for(int i=1;i<=n;++i) if(indu[i]==0) mn++,q[++mx]=i;
    int head=1;
    while(head<=mx)
    {
        int cur=q[head];head++;
        if(die[aim[cur]]) continue;
        die[aim[cur]]=1;
        int live=aim[aim[cur]];
        indu[live]--;
        undie[live]=1;
        if(indu[live]==0) q[++mx]=live;
    }
    for(int i=1;i<=n;++i) if(indu[i] && !die[i]) {
        int len=0,flag=0;    
        for(int j=i;!die[j];j=aim[j]) {
            len++;
            flag|=undie[j];
            die[j]=1;
        }
        if(!flag && len>1) mn++;
        mx+=len/2;
    } 
    printf("%d %d",n-mx,n-mn);
    return 0;
} 

 

 
posted @ 2018-10-03 21:20  qseer  阅读(265)  评论(1编辑  收藏  举报