bzoj 1086 [SCOI2005]王室联邦 贪心+构造

题面

题目传送门

解法

思路比较清晰的构造题

  • 感觉这道题并不能准确说是一个贪心,但是好像用了贪心的思想
  • 因为多个省可以共用省会,所以我们可以考虑尽量将一个点作为多个省会
  • 因为题目并没有要求使最后的省个数尽量小,所以我们可以这样构造:只要出现一组合法的省,直接把这个省单独立出来
  • 那么,问题就转化成如何找一个合法的省。可以发现,我们可以尽量让一棵子树的根作为省会,因为这样一定能保证这个省是合法的。
  • 所以,现在我们就得到了一个大致的算法:假设当前dfs到点 x ,先分 x 的儿子,如果存在可以直接把儿子的子树变成一个省的情况,直接将 x 变成省会,那些可以作为一个省的点直接被删掉,如此做即可。
  • 然后,我们考虑为什么这样一定可以满足省内的点 3 b
  • 显然,每一次只要出现一个大小不小于 b 的省就直接划分,如果出现一个省大小超过了 3 b ,那么就一定可以在它大小不小于 b 的时候直接被划分出来,所以这样就可以保证这个算法是正确的
  • 时间复杂度: O ( n 2 )

代码

#include <bits/stdc++.h>
#define N 1010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
    int next, num;
} e[N * 3];
int n, b, l, cnt, tot, st[N], siz[N], col[N], ans[N];
void add(int x, int y) {
    e[++cnt] = (Edge) {e[x].next, y};
    e[x].next = cnt;
}
void dfs(int x, int fa) {
    st[++l] = x;
    for (int p = e[x].next; p; p = e[p].next) {
        int k = e[p].num;
        if (k == fa) continue; dfs(k, x);
        if (siz[x] + siz[k] >= b) {
            ans[++tot] = x, siz[x] = 0;
            while (st[l] != x) col[st[l--]] = tot;
        } else siz[x] += siz[k];
    }
    siz[x]++;
}
void update(int x, int fa, int c) {
    if (col[x]) c = col[x]; else col[x] = c;
    for (int p = e[x].next; p; p = e[p].next)
        if (e[p].num != fa) update(e[p].num, x, c);
}
int main() {
    read(n), read(b); cnt = n;
    for (int i = 1; i < n; i++) {
        int x, y; read(x), read(y);
        add(x, y), add(y, x);
    }
    if (n < b) return cout << "0\n", 0;
    dfs(1, 0); if (!tot) tot++;
    col[1] = tot; update(1, 0, col[1]); cout << tot << "\n";
    for (int i = 1; i <= n; i++) cout << col[i] << ' '; cout << "\n";
    for (int i = 1; i <= tot; i++) cout << ans[i] << ' '; cout << "\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/81979833