• P4551 最长异或路径


    题目

    传送门

    思路

    别在意这是一道紫题,其实还是能做的

    首先要知道:异或运算满足交换律,结合律,a xor a = 0,一个点A到另一个点B的异或路径长度等于(A到C的异或路径长度 xor B到C的异或路径长度),其中C为任一点

    为什么?

    假设C是树的根,后者只是比前者多跑了2遍C到lca(A,B)的路径,也就是这条路径上的边会被异或两边,又因为同一个数异或的结果为0,所以这多跑的2遍对结果无影响

    所以,我们随便选一个点作为根(这里就用1号点),求出所有点到1号点的异或路径长度,存在dis[]中,这样,我们就能O(1)求出两个点之间的异或路径长度

    到此,原问题转化为:

    找一对i,j,使dis[i] ^ dis[j]最大(" ^ "表示异或)

    01trie是解决这种异或问题的利器,但是,怎么找呢?

    先说01trie:按照dis[i]从二进制下高位到低位,从根到叶子的顺序建树(懒得画图了,自己看代码理解下)

    然后?

    我在没看题解时的思路:

    1. 从trie的根结点开始向下找,直到遇到分支(因为此时高位是1,高位大的一定大)
    2. 找到分支后,用BFS+贪心查找最优解(尽量让两个数异或后高位为1)
    3. 但是,最坏情况下,时间复杂度是可以去到2^30的

    因此,看了一波题解

    正解:

    1. O(n)枚举每一个dis[i]
    2. O(30)在trie中贪心查找另一个trie[j],使trie[i] ^ trie[j]最大(这里的贪心其实就是让异或出来的结果高位更大,这也就决定了如何建trie树)

    反思

    其实我的思路离正解已经很近了,可以说只差了最后一步,但是失之毫厘差之千里,复杂的几乎就是O(n^2)的纯暴力和正解的区别,应该从多方面思考问题的解,优化程序中复杂度最高的地方

    代码

    #include <iostream>
    #include <cstdio>
    #define nn 100010
    using namespace std;
    int read() {
    	int re = 0 , sig = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')sig = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re * sig;
    }
    struct ednode{//链式前向星
    	int nxt , w , to;
    }ed[nn * 2];
    int head[nn];
    inline void addedge(int u , int v , int w) {
    	static int top = 1;
    	ed[top].to = v , ed[top].w = w , ed[top].nxt = head[u] , head[u] = top;
    	++top;
    }
    
    int dis[nn];
    int n;
    int trie[nn * 30][3];
    
    
    void dfs(int x , int pre) {//处理出dis数组
    	for(int i = head[x] ; i ; i = ed[i].nxt) {
    		if(ed[i].to == pre)continue;
    		dis[ed[i].to] = dis[x] ^ ed[i].w;
    		dfs(ed[i].to , x);
    	}
    }
    void build() {//建trie树
    	int top = 1;
    	for(int i = 1 ; i <= n ; i++) {
    		int tmp = dis[i];
    		int p = 1;
    		for(int j = 30 ; j >= 0 ; j--) {
    			int x = (tmp >> j) & 1;
    			if(trie[p][x] == 0)
    				trie[p][x] = ++top;
    			p = trie[p][x];
    		}
    	}
    }
    int GetAns() {
    	int ans = 0;
    	for(int i = 1 ; i <= n ; i++) {//枚举每一个dis
    		int tmp = dis[i];
    		int res = 0;
    		int p = 1;
    		for(int j = 30 ; j >= 0 ; j--) {//找到最优的另一个dis,满足它和dis[i]的异或值最大
    			if(trie[p][!((tmp >> j) & 1)] != 0) {
    				res += (1 << j);
    				p = trie[p][!((tmp >> j) & 1)];
    			}
    			else
    				p = trie[p][(tmp >> j) & 1];
    		}
    		if(res > ans)
    			ans = res;
    	}
    	return ans;
    }
    
    int main() {
    	n = read();
    	for(int i = 1 ; i < n ; i++) {
    		int u , v , w;
    		u = read();	v = read();	w = read();
    		addedge(u , v , w);
    		addedge(v , u , w);
    	}
    	dfs(1 , 0);
    	build();
    	cout << GetAns();
    	return 0;
    }
    /*洛谷样例2
    10
    1 2 12188248
    2 3 2060207469
    1 4 960096258
    1 5 681126748
    3 6 719580677
    6 7 2084644229
    4 8 730246277
    1 9 668729523
    9 10 1055107866
    
    2084644229
    
    */
    
  • 相关阅读:
    关于windows CE Platform Builder中模拟器的限制
    送给每天都用电脑的人
    用eVC4开发SmartPhone、Pocket PC程序之 开发工具下载、安装、配置
    在手机上显示图片
    如何禁止回车的使用
    弹出窗口的方法
    Request.Form同Request.querystring的区别.txt
    找前几天
    手机上可移动的图片
    httphandler是做什么的?
  • 原文地址:https://www.cnblogs.com/dream1024/p/14026438.html
Copyright © 2020-2023  润新知