codeforces 888G.Xor-MST(01字典树+贪心+最小异或生成树)

来源:互联网 发布:数据库 图标 编辑:程序博客网 时间:2024/06/08 12:46

给你n个点,每条边的边权是两个点的异或和,问你形成最小生成树,需要的代价是多少。(n200000ai230

思路:把数都插到字典树里面,然后考虑两个数的合并,最小代价的话,应该是尽可能相同的多,所以可以看做是两个子树的合并,那么插的时候记录一个siz,采取dfs,发现点root有左右儿子结点的时候,就可以合并了,合并的方式是枚举较小的子树中的每一个数,然后跑到另一个子树里去查最小异或和,更新答案。

#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 200000 + 5;typedef long long LL;long long ans, cost;struct Trie{    int ch[32*maxn][2], siz[32*maxn], val[32*maxn], num[32*maxn], sz;    int up = 30;    init(){sz = 1;memset(ch[0], 0, sizeof(ch[0]));}    void Insert(int x)    {        int u = 0;        for(int i = up; i >= 0; i--)        {            int c = ((x >> i) & 1);            if(ch[u][c] == 0)            {                memset(ch[sz], 0, sizeof(ch[sz]));                num[sz] = val[sz] = 0;                ch[u][c] = sz++;            }            u = ch[u][c];            num[u] ++;        }        val[u] = x;    }    int getSize(int x)    {        if(!ch[x][0] && !ch[x][1])  return siz[x] = 1;        if(ch[x][0])    siz[x] += getSize(ch[x][0]);        if(ch[x][1])    siz[x] += getSize(ch[x][1]);        return siz[x];    }    void dfs(int x)    {        if(ch[x][0]) dfs(ch[x][0]);        if(ch[x][1]) dfs(ch[x][1]);        if(ch[x][0] && ch[x][1])        {//要合并root = x,这个结点的左右儿子。            cost = INF;            int lson = ch[x][0], rson = ch[x][1];            if(siz[lson] < siz[rson])   calc(lson, x);            else calc(rson, x);            ans += cost;        }    }    void calc(int x, int pre)    {//遍历root左右子树中较小一颗的所有数。        if(ch[x][0]) calc(ch[x][0], pre);        if(ch[x][1]) calc(ch[x][1], pre);        if(!ch[x][0] && !ch[x][1])        {            int now = query(val[x], pre);            if(cost > (val[now] ^ val[x]))            {                cost = (val[now] ^ val[x]);            }        }    }    int query(int x, int pre)    {//pre标记之前要合并左右儿子的根结点root。        int u = 0;        for(int i = up; i >= 0; i--)        {            int c = ((x >> i) & 1);            if(ch[u][c] == 0)   c = 1 - c;            if(u == pre)    c = 1 - c;            u = ch[u][c];        }        return u;    }}trie;int main(){    trie.init();    int n;    scanf("%d", &n);    for(int i = 1; i <= n; i++)    {        int x;        scanf("%d", &x);        trie.Insert(x);    }    trie.getSize(0);    trie.dfs(0);    printf("%lld\n", ans);    return 0;}
原创粉丝点击