BZOJ 1124: [POI2008]枪战Maf
1124: [POI2008]枪战Maf
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 617 Solved: 236
[Submit][Status][Discuss]
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
HINT
Source
分析
很有意思的一道贪心题目,主要是代码实现上的细节挺多,要不就是今晚太困了……
两个问题要分开分析,有不同的贪心思路。
首先,枪杀关系类似于有向图。按照联通块进行划分,其可以分为大致三种情况——
-
单独一个点形成的自环。显然这个点必定会死,不论是最多死亡人数还是最少死亡人数,都会使答案+1。
-
一条链,最后的一个点可能是个自环,类似于1->2->3->4->5->5。显然这条链上除了1,其他点通过合理的顺序都可以被枪杀,所以最多死亡人数是len-1。因为1不会死,我们贪心的让1先杀死2,这样3就可以活下来;因为3可以活下来,所以贪心的杀死4,这样5就可以活下来。依次类推,得到一个贪心思路——如果当前认为一个点可以活,则贪心的杀死他的枪杀目标,给他枪杀目标的目标以更大的生存机会。如果这样发现,其枪杀目标的目标已经没有入边,则认为其也是可以活下来的,继续这一过程。
-
最为复杂的是基环内向树和简单的有向环。如果是环,那么至少要有一个点活着,最多只能有(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 }
@Author: YouSiki