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;
}
posted @ 2018-10-17 10:52  EvalonXing  阅读(162)  评论(0编辑  收藏  举报