zoukankan      html  css  js  c++  java
  • CF888G Xor-MST(01trie,MST)

    前沿

    刚学习了 Borůvka 生成树算法,去写几道题,结果这道题用的是 01Trie,可能是用到了这个算法的思想吧。

    题目链接

    题目大意

    (n) 个点的无向完全图,每条边的权值定义为它两个端点的异或值,求这个图的最小生成树

    题目解析

    生成树算法一般有 Kruskal,Prim 和 Borůvka 算法,一般对于边权是由点权0转变过来的求 MST 的题目,一般采用 Borůvka 求解。

    Borůvka 是一种维护联通块,每轮连多条边的算法,因为最多只会进行 (log) 轮,所以复杂度是 (mlogn) 的,当我们用一些数据结构维护后,复杂度可以降低,从而达到通过本题的复杂度。

    当然一些题只是用的此算法的思想,并没有真正的在代码上写出此算法。

    回到本题,我们显然可以建一棵 01Trie 出来。

    我们发现如果两个节点共同的部分(即 01trie 上的lca)深度越深,显然它们的异或值越小,所以我们可以利用贪心的思想,即在trie树上贪心。

    对于当前的一个节点,如果它的左右儿子都存在,那么显然左子树和右子树一定会在此节点去合并成一个集合,那么我们可以去找到左子树和右子树中的哪两个值异或的值最小,但是这样显然复杂度是不对劲的。

    所以我们可以采用启发式合并的思想,每次把小的去往大的里面找,这样就可以做到正常复杂度,复杂度大概是 (O(nlog^2n))

    在合并两棵子树时, 我们把 size 较小的那棵树的所有节点拿出来, 然后在另一棵子树上贪心地走, 最后对较小子树的所有节点的结果取一个最小值就好了。

    相当于一棵 01Trie 在 另一棵 01Trie 上去匹配。

    题目代码

    // by longdie 
    #include <bits/stdc++.h> 
    #define ll long long 
    using namespace std;
    const int N = 2e5 + 5; 
    ll Min; 
    int n, tot, ch[N*20][2], siz[N*20];
    void ins(int x) {
    	int now = 0; siz[now]++; 
    	for(register int i = 29; i >= 0; --i) {
    		int c = (x >> i & 1); 
    		if(!ch[now][c]) ch[now][c] = ++tot; 
    		now = ch[now][c], siz[now]++; 
    	}
    }
    void merge(int u, int v, int dep, ll sum) {
    	if(sum >= Min) return; 
    	if(dep < 0) { Min = min(Min, sum); return; } 
    	if(ch[u][0]) {
    		if(ch[v][0]) merge(ch[u][0], ch[v][0], dep-1, sum); 
    		else if(ch[v][1]) merge(ch[u][0], ch[v][1], dep-1, sum+(1<<dep)); 
    	}
    	if(ch[u][1]) {
    		if(ch[v][1]) merge(ch[u][1], ch[v][1], dep-1, sum); 
    		else if(ch[v][0]) merge(ch[u][1], ch[v][0], dep-1, sum+(1<<dep)); 
    	}
    }
    ll query(int u, int dep) {
    	if(dep < 0) return 0; 
    	ll res = 0; 
    	if(ch[u][0]) res += query(ch[u][0], dep - 1);
    	if(ch[u][1]) res += query(ch[u][1], dep - 1);
    	if(ch[u][0] && ch[u][1]) {
    		res += 1 << dep, Min = 0x3f3f3f3f3f3f3f3f; 
    		if(siz[ch[u][0]] <= siz[ch[u][1]]) merge(ch[u][0], ch[u][1], dep - 1, 0); 	
    		else merge(ch[u][1], ch[u][0], dep - 1, 0); 
    		res += Min; 
    	}
    	return res; 
    }
    signed main() {
    	scanf("%d", &n); 
    	for(register int i = 1; i <= n; ++i) {
    		int x; scanf("%d", &x), ins(x); 
    	}
    	printf("%lld
    ", query(0, 29)); 
    	return 0; 
    }
    
    
  • 相关阅读:
    Java实现 SSL双向认证
    2018-2019-2 网络对抗技术 20165336 Exp 9 Web安全基础
    2018-2019-2 网络对抗技术 20165336 Exp 8 Web基础
    2018-2019-2 网络对抗技术 20165336 Exp7 网络欺诈防范
    2018-2019-2 网络对抗技术 20165336 Exp6 信息搜集与漏洞扫描
    2018-2019-2 网络攻防技术 20165336 Exp5 MSF基础应用
    2018-2019-2 网络对抗技术 20165336 Exp4 恶意代码分析
    2018-2019-2 网络对抗技术 20165336 Exp3 免杀原理与实践
    2018-2019-2 网络对抗技术 20165336 Exp2 后门原理与实践
    2018-2019-2 网络对抗技术 20165336 Exp1 PC平台逆向破解
  • 原文地址:https://www.cnblogs.com/longdie/p/14506916.html
Copyright © 2011-2022 走看看