2020牛客多校第五场B题Graph(最小异或和生成树 字典树 贪心 分治)

题目链接 https://ac.nowcoder.com/acm/contest/5670/B

题意:输入一个n点不含环的图,你可以加、删边不限次数,但是要保证1.图是连通的2.成环的边异或值为0,输出最小的图的边值和。

题解:我们以第一个点为根求最小异或和生成树。首先,我们以第一个点数字0为根,dfs求出下面所有边与父亲边的异或和关系,套用最小异或生成树板子就可以过题。

最小异或生产树,是对于每个边的值按照二进制建立01字符串Trie树,利用贪心的思想分治解决问题。

贴一个大佬 https://blog.csdn.net/qq_43857314/article/details/107645865

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=2e5+7;

struct node{
    int val;
    int v ; //下一个店 
    int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[maxn];
int cnt;
int head[maxn];   //从1点开始 
void add(int u,int v,int val){
    edge[cnt].val=val;
    edge[cnt].v=v;    //edge[i]表示第i条边的终点 
    edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置 
    head[u]=cnt++;
}

const int base=30;
struct Trie {
    int tr[maxn * base][2];
    vector<int> e[maxn * base]; int tot;
    void insert(int x) {
        int u = 0;
        for (int i = base; i >= 0; i--) {
            int t = (x >> i) & 1;
            if (!tr[u][t]) tr[u][t] = ++tot;
            u = tr[u][t];
            e[u].push_back(x);
        }
    }
    int ask(int id, int d, int x) {  //返回位置 
        if (d < 0) return 0;
        int t = (x >> d) & 1;
        if (tr[id][t]) return ask(tr[id][t], d - 1, x);
        return ask(tr[id][t ^ 1], d - 1, x) + (1 << d);
    }
    ll solve(int id, int d) {
        int l = tr[id][0], r = tr[id][1];
        ll ans = 0;
        if (l && r) {
            if (e[l].size() > e[r].size()) swap(l, r);
            int mi = numeric_limits<int>::max(); //inf
            for (int i = 0; i < e[l].size(); ++i)
                mi=min(mi, ask(r, d - 1, e[l][i]));
            ans += mi + (1 << d);
        }
        if (l) ans += solve(l, d - 1);
        if (r) ans += solve(r, d - 1);
        return ans;
    }
} tri;

int col[maxn];
void dfs(int u,int fa){
    for(int i=head[u];~i;i=edge[i].next){
        if(edge[i].v!=fa){
            col[edge[i].v]=col[u]^edge[i].val;
            dfs(edge[i].v,u);
        }
    }
}
int main(){
    cnt=0;
    int n;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    int x,y,w;
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w);
        add(y,x,w);
    }
    dfs(0,0);
    
     for(int i=0;i<n;i++){
         tri.insert(col[i]);
     }
    printf("%lld\n",tri.solve(0,base));
    return 0;
}

 

posted @ 2020-08-05 16:00  杰瑞与汤姆  阅读(190)  评论(0编辑  收藏  举报