[BZOJ 1124][POI 2008] 枪战 Maf
1124: [POI2008]枪战Maf
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 659 Solved: 259
[Submit][Status][Discuss]Description
有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。
Input
输入n人数<1000000 每个人的aim
Output
你要求最后死亡数目的最小和最大可能
Sample Input
8
2 3 2 2 6 7 8 5Sample Output
3 5
不看数据范围可能看起来像是缩点树归的样子, 但是平常的 $O(n^2)$ 树归在这里显然需要 $\text{真-}Owys$ 才能过( $\text{-}Owys$大法好, $n^2$ 过 $5 \times 10^5$ )(雾
然后按照图论题的一般套路就该找规律了w(找不到规律就成了神tm不可做题了)
经过一波 $Tarjan$ 缩点后再YY我们可以得到以下结论:
首先对于最大死亡数来说:
当 $SCC$ 为一个点的最大死亡数在存在入边时为$1$, 否则为 $0$.
当 $SCC$ 为一个环的最大死亡数在存在入边时为 $SCC$ 中的结点数 $size$ , 否则为 $size-1$ (有一个点是无法杀死的)
当环存在入边时还要加上连入的环的结点数再减去其中的零入度结点(因为零入度结点不可能死亡).
然后是最小死亡数:
首先零入度结点必定无法杀死, 而零入度结点的目标必死. 所以我们可以在预处理时将零入度结点压入一个队列, 然后删除它和它的目标, 更新答案, 并将删掉的结点的目标的入度减去 $1$ . 因为杀死必死的人后可能它的目标的入度变成 $0$ 然后变成新的存活结点. 新的存活结点入队. 如此处理直至队列为空.
然后我们可以发现所有的链都会在这个过程中被处理掉, 也就是说图里应该删得只剩下几坨环了. 然后这时我们可以发现对于环来说, 若环中结点数为 $s$ , 则至少要死去 $\left \lceil \frac {s}{2} \right \rceil$ 个结点. (每次刚好隔开杀手, 隔一个死一个w)
如此处理即可求出最终解.
辣鸡分类讨论吃枣药丸(╯‵□′)╯︵┻━┻(雾
参考代码
1 #include <stack> 2 #include <queue> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <iostream> 7 #include <algorithm> 8 9 const int MAXN=1000010; 10 11 int n; 12 int scc; 13 int Time; 14 int maxA; 15 int minA; 16 int dfn[MAXN]; 17 int low[MAXN]; 18 int size[MAXN]; 19 int belong[MAXN]; 20 int target[MAXN]; 21 int inDegree[MAXN]; 22 int inDegreeSCC[MAXN]; 23 24 bool visited[MAXN]; 25 26 std::stack<int> s; 27 28 void Initialize(); 29 void DFS(int); 30 31 int main(){ 32 std::queue<int> live; 33 Initialize(); 34 for(int i=1;i<=n;i++){ 35 if(inDegree[i]==0) 36 live.push(i); 37 } 38 while(!live.empty()){ 39 int top=live.front(); 40 live.pop(); 41 visited[top]=true; 42 if(visited[target[top]]) 43 continue; 44 minA++; 45 visited[target[top]]=true; 46 if((--inDegree[target[target[top]]])==0&&!visited[target[target[top]]]) 47 live.push(target[target[top]]); 48 } 49 for(int i=1;i<=n;i++){ 50 int cnt=0; 51 if(!visited[i]){ 52 int x=i; 53 do{ 54 visited[x]=true; 55 cnt++; 56 x=target[x]; 57 }while(x!=i); 58 } 59 if(cnt>0) 60 minA+=(cnt+1)/2; 61 } 62 memset(visited,0,sizeof(visited)); 63 for(int i=1;i<=n;i++){ 64 if(dfn[i]==0) 65 DFS(i); 66 } 67 for(int i=1;i<=n;i++){ 68 if(target[i]==i||belong[i]!=belong[target[i]]) 69 inDegreeSCC[belong[target[i]]]++; 70 } 71 for(int i=1;i<=scc;i++){ 72 if(inDegreeSCC[i]>0) 73 maxA+=size[i]; 74 else 75 maxA+=size[i]-1; 76 } 77 printf("%d %d\n",minA,maxA); 78 return 0; 79 } 80 81 void DFS(int root){ 82 dfn[root]=low[root]=++Time; 83 visited[root]=true; 84 s.push(root); 85 if(visited[target[root]]){ 86 low[root]=std::min(low[root],dfn[target[root]]); 87 } 88 else if(dfn[target[root]]==0){ 89 DFS(target[root]); 90 low[root]=std::min(low[root],low[target[root]]); 91 } 92 if(low[root]==dfn[root]){ 93 scc++; 94 int top; 95 do{ 96 top=s.top(); 97 belong[top]=scc; 98 size[scc]++; 99 visited[top]=false; 100 s.pop(); 101 }while(top!=root); 102 } 103 } 104 105 inline void Initialize(){ 106 scanf("%d",&n); 107 for(int i=1;i<=n;i++){ 108 scanf("%d",target+i); 109 inDegree[target[i]]++; 110 } 111 }
本博客已弃用, 新个人主页: https://rvalue.moe, 新博客: https://blog.rvalue.moe