BZOJ1124: [POI2008]枪战Maf
为防止河蟹,“打”用“attack”代替
手玩一下发现他大概是可以贪心的
贪心的规则如下:
在求 maxans 时,分为以下情况:
环:siz - 1
自环:1
树:siz - 叶子数
基环树:siz - 叶子数
所以 maxans 就可以简化成 (n - 叶子数 - 非自环数)
在求 minans 时,分为以下情况:
环:(siz + 1) / 2
树:从叶子向上一层一层 attack
基环树:从叶子向上一层一层 attack
貌似这样就没问题了,可以拿一个很像拓扑排序的东西写了
多画几个图会发现,对于基环树求 minans时,
叶子的深度奇偶不同造成的情况也不同
下面讨论一下

对于这样的一张图,直接从叶子开始按标号顺序 attack 是可以得到期望的最小答案的
那接下来考虑另一种情况,

当环下的树的层数为偶数且没有处于奇数层的叶子时,
从叶子开始 attack ,中间某一步会剩下环,可以直接用上面对环讨论的情况做
如果是写 dfs 的话按照深度为奇数的情况做也是可以的...
下面简单证明一下求 minans 时基环树的贪心:

还拿这张图来说就行
考虑为什么要让圈中的点 attack 环上的点
显然,如果不让他 attack,他一定要被 attack
考虑两种情况的贡献,
首先即让他 attack 又让他被 attack 一定是不优的,因为求的是 minans
将当前树的答案设为 tans
若让他 attack,环的答案不变,tans 一定不变
若不让他 attack,环的答案不变,tans +1
为什么无论如何环的答案不会变呢?
回顾之前求环的答案的式子时,
它的 minans 其实就是从一个点开始顺着环的方向 attack
就是说你把环 attack 散了 和 保留完整的再求 minans 时都是一样的
所以这题就这么做就可以了
代码不写 dfs 实现起来比较恶心...
我是用并查集求的环,其他的代码里都有注释
反正都是中国式英语实在不懂机翻也是很准的= =...
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <queue>
using namespace std;
const int MAXN = 1000005;
int n, top, minans, maxans;
int to[MAXN], fa[MAXN], siz[MAXN];
int ind[MAXN], stk[MAXN], len[MAXN];
int col[MAXN];
bool inc[MAXN], hascir[MAXN], GG[MAXN], chked[MAXN];
// 'stk' : storging circles' vertices
// 'len' : storging circles' length
// 'col' : marking that this vertice belongs to which circle
// 'inc' : vertice x is/isn't in a circle
// 'hascir' : the circle based on vertice x is/isn't a complete circle
// 'GG' : vertice x is/isn't alive
// 'chked' : whether vertice x has been checked
queue<int> q;
inline int rd() {
register int x = 0;
register char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) {
x = x * 10 + (c ^ 48);
c = getchar();
}
return x;
}
int findfa(int x) {
return ((fa[x] == x) ? (x) : (fa[x] = findfa(fa[x])));
}
inline bool link(int x, int y) {
register int fx = findfa(x), fy = findfa(y);
if (fx == fy) return false;
if (siz[fx] > siz[fy]) {
fa[fy] = fx;
siz[fx] += siz[fy];
} else {
fa[fx] = fy;
siz[fy] += siz[fx];
}
return true;
}
inline void markcir(int x) {
register int cur = to[x];
inc[x] = true;
len[x] = 1;
col[x] = x;
while (cur != x) {
inc[cur] = true;
col[cur] = x;
cur = to[cur];
++len[x];
}
if (len[x] == siz[findfa(x)] && len[x] != 1) {
--maxans;
}
return;
}
int main() {
n = maxans = rd();
for (int i = 1; i <= n; ++i) {
fa[i] = i;
siz[i] = 1;
}
for (int i = 1; i <= n; ++i) {
to[i] = rd();
++ind[to[i]];
}
for (int i = 1; i <= n; ++i) {
if (!link(to[i], i)) {
hascir[i] = true;
stk[++top] = i; //count the circles and trees with circle
}
}
for (int i = 1; i <= top ;++i) //mark all the circles
markcir(stk[i]);
for (int i = 1; i <= n; ++i) if (!ind[i]) {
q.push(i);
--maxans;
}
while (!q.empty()) {
int x = q.front(), y = to[x]; q.pop();
if (!chked[x]) --siz[findfa(x)];
chked[x] = true; //x has been checked
--ind[y];
if (!GG[x]) {
if (!GG[y]) {
GG[y] = true; //y has been slain
++minans;
q.push(y);
}
if (col[y]) {
hascir[col[y]] = false; //x broke the circle
}
} else { //if y is leagal for checking
if (!ind[y] && !GG[y] && !chked[y]) q.push(y);
}
}
for (int i = 1; i <= top; ++i) //if the circle is a complete circle
if (hascir[col[stk[i]]]) minans += (len[col[stk[i]]] + 1) / 2;
printf("%d %d\n", minans, maxans);
return 0;
}

浙公网安备 33010602011771号