2020牛客多校第五场B-Graph

https://ac.nowcoder.com/acm/contest/5670/B

题意

给一棵树,每条边有边权。可以任意加边和删边,但要满足任何时刻图连通,而且任何一个环的边权异或和为0。求操作后最小权值和

题解

任意两点间连边的权值是固定的,可以预处理给每个点赋值点权,两点间的边权就是点权的异或,点权直接dfs一遍取链上的异或和即可。

然后就是异或最小生成树模板

对于异或最小生成树,有Boruvka算法,先对于每个点,选择在所有与之相连的边中,权值最小的边,并将这条边加入到最小生成树中。显然这样连出来的边会形成一个森林,并且连边后连通块个数至少减半。然后我们将每个连通块再看成一个点,重复以上算法即可。时间复杂度O(mlogn)。

对于此题,我们把所有点权扔到Trie里,对于每一层,有两种情况,

  1. 一种全为0,或者全为1,那么这一位无需考虑,不会对答案产生贡献。

  2. 一部分为0,一部分为1,那么两组之间要有一条边把这两个组相连,在字典树上找到异或值最小的作为连接两个集合的边,加入答案。对两组分别递归求解。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
    inline char read() {
    #ifdef _WIN32
        return getchar();
    #endif
        static const int IN_LEN = 1 << 18 | 1;
        static char buf[IN_LEN], *s, *t;
        return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
    }
    template <typename _Tp> inline READ & operator >> (_Tp&x) {
        static char c11, boo;
        for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
            if(c11 == -1) return *this;
            boo |= c11 == '-';
        }
        for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
        boo && (x = -x);
        return *this;
    }
} in;

const int N = 1e5 + 50;
int a[N];
struct node {
    int v,  w;
    node(int v = 0, int w = 0): v(v), w(w) {}
};
vector<node> G[N];
int cnt = 0;
void dfs(int u, int fa) {
    for (auto nx : G[u]) {
        if (nx.v == fa) continue;
        a[nx.v] = a[u] ^ nx.w;
        dfs(nx.v, u);
    }
}
int trie[N << 5][2];
int tot = 0;
void ins(int x) {
    int rt = 0;
    for (int i = 30; i >= 0; i--) {
        int now = (x >> i) & 1;
        if (!trie[rt][now]) trie[rt][now] = ++tot;
        rt = trie[rt][now];
    }
}
int qry(int x) {
    int ans = 0, rt = 0;
    for (int i = 30; i >= 0; i--) {
        int now = (x >> i) & 1;
        if (trie[rt][now]) rt = trie[rt][now];
        else {
            rt = trie[rt][now ^ 1];
            ans |= (1 << i);
        }
    }
    return ans;
}
ll ans = 0;
void calc(int l, int r, int dep) {
    if (dep < 0 || l >= r) return;
    int mid = l - 1;
    while (mid < r && !((a[mid + 1] >> dep) & 1)) mid++;
    calc(l, mid, dep - 1);
    calc(mid + 1, r, dep - 1);
    if (mid == l - 1 || mid == r) return;
    tot = 0;
    for (int i = l; i <= mid; i++) ins(a[i]);
    int res = 0x7fffffff;
    for (int i = mid + 1; i <= r; i++) res = min(res, qry(a[i]));
    ans += res;
    for (int i = 0; i <= tot; i++) trie[i][0] = trie[i][1] = 0;
}
int main() {
    int n; in >> n;
    for (int i = 1; i < n; i++) {
        int u, v; ll w;
        in >> u >> v >> w;
        u++; v++;
        G[u].push_back(node(v, w));
        G[v].push_back(node(u, w));
    }
    dfs(1, 0);
    sort(a + 1, a + n + 1);
    calc(1, n, 30);
    printf("%lld\n", ans);
    return 0;
}
posted @ 2020-09-01 14:50  Artoriax  阅读(135)  评论(0编辑  收藏  举报