洛谷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 }

 

posted @ 2018-05-16 17:59  mrclr  阅读(187)  评论(0编辑  收藏  举报