BZOJ 1124: [POI2008]枪战Maf

Posted You Siki

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1124: [POI2008]枪战Maf相关的知识,希望对你有一定的参考价值。

1124: [POI2008]枪战Maf

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 617  Solved: 236
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

8
2 3 2 2 6 7 8 5

Sample Output

3 5

HINT

 

Source

 
[Submit][Status][Discuss]

 

分析

很有意思的一道贪心题目,主要是代码实现上的细节挺多,要不就是今晚太困了……

两个问题要分开分析,有不同的贪心思路。

首先,枪杀关系类似于有向图。按照联通块进行划分,其可以分为大致三种情况——

  1. 单独一个点形成的自环。显然这个点必定会死,不论是最多死亡人数还是最少死亡人数,都会使答案+1。

  2. 一条链,最后的一个点可能是个自环,类似于1->2->3->4->5->5。显然这条链上除了1,其他点通过合理的顺序都可以被枪杀,所以最多死亡人数是len-1。因为1不会死,我们贪心的让1先杀死2,这样3就可以活下来;因为3可以活下来,所以贪心的杀死4,这样5就可以活下来。依次类推,得到一个贪心思路——如果当前认为一个点可以活,则贪心的杀死他的枪杀目标,给他枪杀目标的目标以更大的生存机会。如果这样发现,其枪杀目标的目标已经没有入边,则认为其也是可以活下来的,继续这一过程。

  3. 最为复杂的是基环内向树和简单的有向环。如果是环,那么至少要有一个点活着,最多只能有(size/2)个点活着。而如果是基环树,至少每条链的尾端要活着,至多可以做到类似于上面的贪心。

 

代码

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3  
  4 #define N 1000100
  5 
  6 int n, to[N], ans1, ans2;
  7 
  8 namespace query1
  9 {
 10     int cnt[N];
 11     int vis[N];
 12     int que[N];
 13     
 14     void solve(void)
 15     {
 16         ans1 = n;
 17         
 18         memset(cnt, 0, sizeof(cnt));
 19         memset(vis, 0, sizeof(vis));
 20         
 21         for (int i = 1; i <= n; ++i)
 22             ++cnt[to[i]];
 23             
 24         for (int i = 1; i <= n; ++i)
 25             if (i == to[i])
 26                 --cnt[i], vis[i] = 1;
 27                 
 28         int head = 0, tail = 0;
 29                 
 30         for (int i = 1; i <= n; ++i)
 31             if (!vis[i] && !cnt[i])
 32                 que[tail++] = i;
 33                 
 34         while (head != tail)
 35         {
 36             int top = que[head++];
 37             
 38             vis[top] = 1; --ans1;
 39             
 40             if (!vis[to[top]])
 41             {
 42                 vis[to[top]] = 1;
 43                 
 44                 if (!vis[to[to[top]]] && --cnt[to[to[top]]] == 0)
 45                     que[tail++] = to[to[top]];
 46             }
 47         }
 48         
 49         for (int i = 1; i <= n; ++i)
 50             if (!vis[i])
 51             {
 52                 int siz = 0;
 53                 
 54                 for (int j = i; !vis[j]; j = to[j])
 55                     vis[j] = 1, ++siz;
 56                     
 57                 ans1 -= (siz >> 1);
 58             }
 59     }
 60 }
 61 
 62 namespace query2
 63 {
 64     int cnt[N];
 65     int vis[N];
 66     int que[N];
 67     int flg[N];
 68     
 69     void solve(void)
 70     {
 71         ans2 = n;
 72         
 73         memset(cnt, 0, sizeof(cnt));
 74         memset(vis, 0, sizeof(vis));
 75         memset(flg, 0, sizeof(flg));
 76         
 77         for (int i = 1; i <= n; ++i)
 78             ++cnt[to[i]];
 79             
 80         for (int i = 1; i <= n; ++i)
 81             if (i == to[i])
 82                 --cnt[i], vis[i] = 1;
 83                 
 84         int head = 0, tail = 0;
 85                 
 86         for (int i = 1; i <= n; ++i)
 87             if (!vis[i] && !cnt[i])
 88                 que[tail++] = i, --ans2;
 89                 
 90         while (head != tail)
 91         {
 92             int top = que[head++]; 
 93             
 94             flg[to[top]] = vis[top] = 1;
 95             
 96             if (--cnt[to[top]] == 0)
 97                 que[tail++] = to[top];
 98         }
 99         
100         for (int i = 1; i <= n; ++i)
101             if (!vis[i])
102             {
103                 int flag = 0;
104                 
105                 for (int j = i; !vis[j]; j = to[j])
106                     vis[j] = 1, flag |= flg[j];
107                     
108                 if (!flag)--ans2;
109             }
110     }
111 }
112 
113 signed main(void)
114 {
115     scanf("%d", &n);
116     
117     for (int i = 1; i <= n; ++i)
118         scanf("%d", to + i);
119     
120     query1::solve();
121     query2::solve();
122     
123     printf("%d %d\\n", ans1, ans2);
124 }
BZOJ_1124.cpp

 

@Author: YouSiki

以上是关于BZOJ 1124: [POI2008]枪战Maf的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1124: [POI2008]枪战Maf

bzoj1124[POI2008]枪战maf tarjan+树规+贪心/线性DP

BZOJ 1124[POI2008]枪战

枪战Maf (bzoj 1124)

[POI2008]枪战Maf题解

枪战Maf[POI2008]