zoukankan      html  css  js  c++  java
  • 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
    ", minans, maxans);
        return 0;
    }
  • 相关阅读:
    工作
    失败
    理想和一些未来的计划
    安静
    重新开始
    如何度过周末
    放假
    WPF学习笔记-数据采集与监控项目01-登录界面
    VS2017-断点感叹号问题,调试代码显示“当前无法命中断点,还没有为该文档加载任何符号”
    WPF-MVVM模式-表现层的UI框架【学习笔记】
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9802743.html
Copyright © 2011-2022 走看看