AGC018F. Two Trees

Posted ichneumon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AGC018F. Two Trees相关的知识,希望对你有一定的参考价值。

题意

给出两棵树,要求给两棵树上相同编号的点赋值,使得每个点的子树权值和的绝对值为1

做法

如果某个编号代表的点在一棵树中的儿子数为奇数而在另一棵树中的儿子数为偶数,那么无论这个位置填什么都不可能,否则可以构造只填0,1,-1的方案。具体做法是,分别考虑两棵树,如果一个点的儿子数为奇数,那么这个点填0;否则对于这个点所在的子树里的奇数儿子点两两配对,表示一个填1另一个填-1。把两棵树的配对情况一起建图,最后得到的必然是二分图,因为任何一个环上的边必然是交替来自两棵树内的配对。故而二染色填数即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 100;

int n, fa[N], fb[N], is_odd[2][N];

struct Edge {
    int e, *h, *nx, *to, V, E;
    Edge(int V, int E):V(V), E(E) {
        e = 0;
        h = new int [V];
        nx = new int [E];
        to = new int [E];
        memset(h, -1, 4 * V);
    }
    ~Edge() {
        delete h;
        delete nx;
        delete to;
    }
    void clear() {
        e = 0;
        memset(h, -1, 4 * V);
    }
    void add(int u, int v) {
        to[e] = v, nx[e] = h[u], h[u] = e++;
        to[e] = u, nx[e] = h[v], h[v] = e++;
    }
} *tr, *bi;

int dfs(int u, int f) {
    vector<int> odd_son;
    for (int i = tr->h[u]; i != -1; i = tr->nx[i]) {
        int v = tr->to[i];
        if (v != f) {
            v = dfs(v, u);
            if (v != 0)
                odd_son.push_back(v);
        }
    }
    if (!is_odd[0][u])
        odd_son.push_back(u);
    for (int i = 0; i < (int)odd_son.size() - 1; i += 2) {
        bi->add(odd_son[i], odd_son[i + 1]);
    }
    return odd_son.size() % 2 == 0 ? 0 : odd_son.back();
}

bool vis[N], col[N];

void coloring(int u, int c) {
    vis[u] = true;
    col[u] = c;
    for (int i = bi->h[u]; i != -1; i = bi->nx[i]) {
        int v = bi->to[i];
        if (!vis[v])
            coloring(v, c ^ 1);
    }
}

int main() {
#ifdef lol
    freopen("f.in", "r", stdin);
    freopen("f.out", "w", stdout);
#endif

    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &fa[i]);
        if (fa[i] != -1) {
            is_odd[0][fa[i]] ^= 1;
        }
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &fb[i]);
        if (fb[i] != -1)
            is_odd[1][fb[i]] ^= 1;
    }
    for (int i = 1; i <= n; ++i) {
        if (is_odd[0][i] ^ is_odd[1][i]) {
            puts("IMPOSSIBLE");
            return 0;
        }
    }
    tr = new Edge(n + 1, (n + 1) * 2);
    bi = new Edge(n + 10, (n + 10) * 4);
    int rt;
    for (int i = 1; i <= n; ++i) {
        if (fa[i] == -1) {
            rt = i;
            continue;
        }
        tr->add(i, fa[i]);
    }
    dfs(rt, -1);
    tr->clear();
    for (int i = 1; i <= n; ++i) {
        if (fb[i] == -1) {
            rt = i;
            continue;
        }
        tr->add(i, fb[i]);
    }
    dfs(rt, -1);
    for (int i = 1; i <= n; ++i)
        if (!vis[i]) {
            coloring(i, 1);
        }
    puts("POSSIBLE");
    for (int i = 1; i <= n; ++i) {
        if (is_odd[0][i]) {
            printf("%d ", 0);
        } else {
            printf("%d ", col[i] ? 1 : -1);
        }
    }
    puts("");

    return 0;
}

 

以上是关于AGC018F. Two Trees的主要内容,如果未能解决你的问题,请参考以下文章

[AGC018 B] Sports Festival 解题报告

AtCoder AGC035F Two Histograms (组合计数容斥原理)

AGC018E - Sightseeing Plan

AGC018D - Tree and Hamilton Path

[AGC018D] Tree and Hamilton Path 题解

617. Merge Two Binary Trees