枪战Maf (bzoj 1124)
Description
有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。
Input
输入n人数<1000000 每个人的aim
Output
你要求最后死亡数目的最小和最大可能
Sample Input
8
2 3 2 2 6 7 8 5
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; }
从0到1很难,但从1到100很容易