洛谷P4551 最长异或路径
传送门:https://www.luogu.org/problem/show?pid=4551
在看这道题之前,我们应懂这道题怎么做:给定n个数和一个数m,求m和哪一个数的异或值最大。
一种很不错的做法是将n个数按二进制从最高位到低位建立一个trie树,然后把m放在trie树上跑一遍。
因为是从高位到低位存的,所以对于每一位,我们只要贪心让这一位的异或值得1。即如果m得当前位是1,就在trie树上找0;否则就找1.若能找到,ans的这一位就是1,否则是0.
如果上述这道水题会了的话,这道题就不难想了。
首先预处理每一个点到根节点的异或距离,为什么这么做呢,看下图
比如点7到根节点的距离dis[7] = f ^ b ^ a,点6到根节点的距离dis[6] = c ^ a,则dis[7] ^ dis[6] = f ^ b ^ c ^ a ^ a = f ^ b ^ c,恰好为6到7的距离。
因此我们就可以像上面这道题一样,将所有dis放到trie树里面,然后再让每一个dis[i]跑一遍trie树,最后结果取max即可。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #include<iostream> 6 #include<queue> 7 #include<vector> 8 #include<cctype> //isdigit 9 using namespace std; 10 typedef long long ll; 11 #define enter printf("\n") 12 const int maxn = 1e5 + 5; 13 const int INF = 0x3f3f3f3f; 14 inline ll read() { 15 ll ans = 0; 16 char ch = getchar(), last = ' '; 17 while(!isdigit(ch)) { 18 last = ch; 19 ch = getchar(); 20 } 21 while(isdigit(ch)) { 22 ans = ans * 10 + ch - '0'; 23 ch = getchar(); 24 } 25 if(last == '-') ans = -ans; 26 return ans; 27 } 28 inline void write(ll x) { 29 if(x < 0) { 30 putchar('-'); 31 x = -x; 32 } 33 if(x == 0) { 34 putchar('0'); 35 return; 36 } 37 int q[100], N = 0; 38 q[1] = 0; 39 while(x) { 40 q[++N] = x % 10; 41 x /= 10; 42 } 43 while(N) { 44 putchar('0' + q[N]); 45 --N; 46 } 47 } 48 49 int n; 50 vector<int> v[maxn], c[maxn]; 51 int cost[maxn]; 52 bool vis[maxn]; 53 void bfs(int s) { //bfs预处理每一个点到根节点的距离 54 queue<int> q; 55 q.push(s); 56 vis[s] = 1; 57 cost[s] = 0; 58 while(!q.empty()) { 59 int now = q.front(); 60 q.pop(); 61 for(int i = 0; i < (int)v[now].size(); ++i) { 62 if(!vis[v[now][i]]) { 63 vis[v[now][i]] = 1; 64 cost[v[now][i]] = cost[now] ^ c[now][i]; 65 q.push(v[now][i]); 66 } 67 } 68 } 69 } 70 struct Trie { 71 int ch[maxn * 35][2], tot; 72 Trie() { 73 memset(ch, 0, sizeof(ch)); 74 tot = 0; 75 } 76 void Insert(int x) { //建树 77 int now = 0; 78 for(int i = 30; i >= 0; --i) { 79 int w = (x >> i) & 1; 80 if(!ch[now][w]) ch[now][w] = ++tot; 81 now = ch[now][w]; 82 } 83 } 84 int query(int x) { 85 int now = 0, ret = 0; 86 for(int i = 30; i >= 0; --i) { 87 int w = (x >> i) & 1; 88 if(!ch[now][!w]) now = ch[now][w]; 89 else { 90 now = ch[now][!w]; //若和x这一位不同的边存在,就走这条边,同时ret的这一位标记成1 91 ret += (1 << i); 92 } 93 } 94 return ret; 95 } 96 } trie; 97 98 int ans = 0; 99 100 int main() { 101 n = read(); 102 for(int i = 1; i < n; ++i) { 103 int a = read(), b = read(), cost = read(); 104 v[a].push_back(b); 105 c[a].push_back(cost); 106 v[b].push_back(a); 107 c[b].push_back(cost); 108 } 109 bfs(1); 110 for(int i = 1; i <= n; ++i) 111 trie.Insert(cost[i]); 112 for(int i = 1; i <= n; ++i) 113 ans = max(ans, trie.query(cost[i])); 114 write(ans); 115 enter; 116 return 0; 117 }