• Codeforces 888G Xor-MST (最小异或生成树)


    给定 n 个点,每个点有个点权,任意两点可以连一条边,边权为两点权的异或值,求最小生成树

    想法一:暴力求出所有边权,然后把边按边权从小到大排序,用kruskal跑最小生成树

    想法二:把边排序后,发现最小的边权就是两个相同的值的异或值(为0),其次就是两个只在第 0 位不同的数的异或值 (为1)....

    把01trie画出来,每个叶子节点就代表一个数,可以发现最小权值的边就是一个叶子节点和它本身(两个相同的数),其次就是lca距离叶子节点为一的两个叶子节点(这两个叶子节点只有第0位不同),再其次就是lca距离叶子节点距离为2的两个叶子节点...

    于是,从最小的边权开始跑kruskal,肯定是在01trie上lca深度最大的两个叶子节点开始连边,在01tie上画图,可以发现对于每颗子树的根节点,一定是其左右子树上的叶子节点先构成了最小生成树后,再从左子树上选一叶子节点与右子树上选一叶子节点连通,于是就可以递归了

    想法三:怎么计算连通左右子树的边的最小权值,可以枚举其中一颗子树上的每一个数,用O(log n)找最小异或数对的方法找与另一颗子树的所有数的异或最小值。其查找次数为O(n log n),所以复杂度为O(n logn logn)

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    const int MAXN = 2e5+7;
    const int INF = 1e9+7;
    int a[MAXN];
    int n;
    int cnt = 0;
    struct NODE{
    	int ptp[2];
    	int L,R,size;
    }trie[MAXN*30];
    void add(int x,int id){
    	int now = 0;
    	for(int i = 30;i>=0;i--){
    		int t = 0;
    		if(x & (1<<i)) t = 1;
    		int &tt = trie[now].ptp[t];
    		if(!tt) tt = ++cnt;
    		now = tt;
    		if(!trie[now].L) trie[now].L = id;
    		trie[now].R = id;
    		trie[now].size = trie[now].R - trie[now].L + 1;
    	}
    }
    int cal(int s,int pos,int x){//s为根节点,s在pos+1位,找和x取0到pos位时的异或最小值 
    	int now = s;
    	int res = 0;
    	for(int i = pos;i>=0;i--){
    		int t = 0;
    		if(x & (1<<i)) t = 1;
    		if(trie[now].ptp[t]){
    			now = trie[now].ptp[t];
    		}
    		else {
    			now = trie[now].ptp[t^1];
    			res |= 1<<i;
    		}
    	}
    	return res;
    }
    long long solve(int s,int pos){//s在pos位 
    	int x = trie[s].ptp[0],y = trie[s].ptp[1];
    	if(x && y){//左右子树都存在 
    		int res = INF;
    		if(trie[x].size < trie[y].size){
    			for(int i = trie[x].L;i <= trie[x].R;i++){//左子树小就枚举左子树 
    				res = min(res,cal(y,pos-2,a[i]) + (1<<pos-1));//x,y在pos-1位 
    			}
    		}
    		else{
    			for(int i = trie[y].L;i <= trie[y].R;i++){//右子树小就枚举右子树 
    				res = min(res,cal(x,pos-2,a[i]) + (1<<pos-1));
    			}
    		}
    		return (long long)res + solve(x,pos-1) + solve(y,pos-1);
    	}
    	else if(x){
    		//只有左子树,递归 
    		return solve(x,pos-1);
    	}
    	else if(y){
    		return solve(y,pos-1);
    	}
    	return 0;
    }
    int main()
    {
    	cin>>n;
    	for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
    	sort(a+1,a+n+1);
    	for(int i = 1;i <= n;i++) add(a[i],i);
    	long long ans = solve(0,31);
    	printf("%lld
    ",ans);
    	return 0;
    } 
    

      

  • 相关阅读:
    matlab中plot画图参数的设置
    TOF相机基本知识
    工业相机接口选择
    工业相机基本知识
    TwinCAT 3中基于UDP协议通讯的C++实现
    投影几何中的无穷远量
    基于CANopen DSP402的运动控制笔记
    C++ new 的用法
    C++中static用法总结
    设计模式——适配器模式(Adapter Pattern)
  • 原文地址:https://www.cnblogs.com/ruanbaitql/p/15468424.html
Copyright © 2020-2023  润新知