[构造] Codeforces 1615D X(or)-mas Tree

题目大意

给定一棵 \(n(1\leq n\leq 2\times 10^5)\) 个点的树,树上有的边有边权,有的边没有。给出 \(m(1\leq m\leq 2\times 10^5)\) 个限制,每次限制了 \(u\)\(v\) 路径上的边权的异或和的二进制位上 \(1\) 的个数是奇数还是偶数。现在要求给没有边权的边赋予边权,使得满足所有限制。

题解

\(f(x)\) 表示 \(x\) 的二进制位上 \(1\) 的个数的奇偶性,即若 \(x\) 的二进制位上有偶数个 \(1\),则 \(f(x)=0\);若 \(x\) 的二进制位上有奇数个 \(1\),则 \(f(x)=1\)。假设 \(u\)\(v\) 的路径上有一系列边权 \(\{w_1,w_2,\cdots,w_k\}\),则每次限制了 \(f(w_1\bigoplus w_2 \bigoplus \cdots \bigoplus w_k)=0\)\(f(w_1\bigoplus w_2 \bigoplus \cdots \bigoplus w_k)=1\)。显然有 \(f(w_1\bigoplus w_2 \bigoplus \cdots \bigoplus w_k)=f(w_1)\bigoplus f(w_2)\bigoplus \cdots \bigoplus f(w_k)\),所以我们可以把原先的边权 \(w\) 看成 \(f(w)\),需要赋予的边权要么是 \(0\) 要么是 \(1\) 即可。

\(s[u]\) 表示 \(u\) 到根的路径上 \(f(w)\) 的异或和,则每次限制了 \(s[u]\bigoplus s[v]=0\)\(s[u]\bigoplus s[v]=1\),对于原先已有边权的边 \((u,v,w)\),则相当于限制了 \(s[u] \bigoplus s[v]=f(w)\)。于是我们可以对所有的这些限制建一张新图,当限制了 \(s[u]\bigoplus s[v]\) 的取值时,在新图的 \(u,v\) 之间直接连一条边,边权是 \(s[u]\bigoplus s[v]\),此时 \(u,v\) 两点的点权 \(s[u],s[v]\) 异或起来应该等于边权。在新图中可以划分出若干连通块,每个连通块中只需要在一开始令某个点 \(u\) 的点权 \(s[u]\) 等于 \(0\)\(1\) 时,这个连通块内所有的点权就已经确定了,都可以通过异或边权转移得到。若无论点权赋成 \(0\)\(1\) 都存在矛盾,则无解。于是我们计算出了一组合法的 \(s[u]\)。最终对于一开始没有边权的边 \((fa,u)\)\(fa\)\(u\) 的父亲,则 \((fa,u)\) 这条边的边权就可以赋值为 \(s[u]\bigoplus s[fa]\)

时间复杂度 \(O(n+m)\)

Code

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

template<typename elemType>
inline void Read(elemType& T) {
    elemType X = 0, w = 0; char ch = 0;
    while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
    T = (w ? -X : X);
}

struct Graph {
    struct edge { int Next, to, w; };
    edge G[800010];
    int head[200010];
    int cnt;

    Graph() :cnt(2) {}
    void clear(int n) {
        cnt = 2;fill(head, head + n + 2, 0);
    }
    void add_edge(int u, int v, int w) {
        G[cnt].w = w;
        G[cnt].to = v;
        G[cnt].Next = head[u];
        head[u] = cnt++;
    }
};
Graph G, G2;
bool vis[200010];
int s[200010];
int T, n, m;

inline int f(int x) { return __builtin_parity(x); }

void Output(int u, int fa) {
    for (int i = G.head[u];i;i = G.G[i].Next) {
        int v = G.G[i].to;
        if (v == fa) continue;
        if (G.G[i].w == -1) printf("%d %d %d\n", u, v, s[u] ^ s[v]);
        else printf("%d %d %d\n", u, v, G.G[i].w);
        Output(v, u);
    }
}

vector<int> buf;

bool DFS(int u) {
    vis[u] = true;
    buf.push_back(u);
    for (int i = G2.head[u];i;i = G2.G[i].Next) {
        int v = G2.G[i].to, w = G2.G[i].w;
        if (vis[v]) {
            if (s[v] ^ s[u] != w) return false;
            continue;
        }
        s[v] = w ^ s[u];
        if (!DFS(v)) return false;
    }
    return true;
}

int main() {
    Read(T);
    while (T--) {
        Read(n);Read(m);
        G.clear(n);
        G2.clear(n);
        for (int i = 1;i <= n - 1;++i) {
            int u, v, w;
            Read(u); Read(v); Read(w);
            G.add_edge(u, v, w);
            G.add_edge(v, u, w);
            if (w != -1) {
                w = f(w);
                G2.add_edge(u, v, w);
                G2.add_edge(v, u, w);
            }
        }
        fill(vis + 1, vis + n + 1, false);
        bool flag = true;
        for (int i = 1;i <= m;++i) {
            int u, v, w;
            Read(u); Read(v); Read(w);
            if (u == v) {
                if (w) flag = false;
                continue;
            }
            G2.add_edge(u, v, w);
            G2.add_edge(v, u, w);
        }
        if (!flag) { printf("NO\n"); continue; }
        for (int u = 1;u <= n;++u) {
            if (vis[u]) continue;
            s[u] = 0; buf.clear();
            if (!DFS(u)) {
                for (auto x : buf) vis[x] = false;
                buf.clear(); s[u] = 1;
                if (!DFS(u)) { flag = false; break; }
            }
        }
        if (!flag) {
            printf("NO\n");
            continue;
        }
        printf("YES\n");
        Output(1, 0);
    }
    return 0;
};

posted @ 2021-12-25 22:15  AE酱  阅读(70)  评论(0编辑  收藏  举报