POJ3764 The xor-longest Path (字典树) 【例题详解】

题意:
  给出一棵树,求树中最长的xor路径。(n<=100000)

输入 点的数量n,之后n-1行代表x点到y点间有一条权值为z的边;

输出 最优解;

 

  这道题与一般的最长路径不相同,因为考虑到了一个陌生的操作——异或;

  异或这种东西和加减乘除还是有区别的,但是还是有一些性质让我们在计算时有据可查;以下是异或运算的性质;

 

  1、交换律

 

  2、结合律(即(a^b)^c == a^(b^c))

 

  3、对于任何数x,都有x^x=0,x^0=x

 

  4、自反性 A XOR B XOR B = A xor  0 = A

 

  好,我们现在可以来看这道题相关的内容;因为题中是一棵树,我们可以像单源最短路那样求出某一个点到所有点的XOR路径;

但是为什么要这样求呢?

我们看图:

比如我现在已经求出了点1到每一个点的异或路径表示为w[2~5].但我现在要枚举到点4到点5之间的XOR路径,怎么办?

其实我直接w[4]^w[5]即可,为什么。其实这里面1和3的路径异或了两次,由于自反性,和没异或是一样的!

所以这个问题就解决了,但是还有一个问题就是:我怎么找最大路径,难道真的要一个一个枚举么?那TLE稳稳地啊;

怎么办,可以把int类型所对应的三十来位01串扔到字典树里;

扔到字典树里有什么用呢?我就可以方便地找出那个最大的啊!

先把每条路径都存进去,然后一个串一个串的找,找什么?

比如说我目前的01串是10110110011,我应该找一个什么样的去异或是最理想的?当然是找最接近01001001100的啦,为什么?因为异或出来明显是一串1嘛;

但是什么叫最接近?一定是得到的1最多么?

不一定,而是较大的那位最有优先权,因为这一位明显比后面的位分量都要足嘛,所以不要走入误区而想多;

 

好,最后我们捋一遍思路,首先,深搜求出某个点到每个点的XOR路径,然后把这些数都按二进制一位一位存到trie树里面,之后枚举点,一位一位地去找出哪两个点可以获得最大的XOR路径,这道题完美地走向结局。

还有一点,写得稍微简单一点,不然容易被卡时限

 

 1 #pragma GCC optimize(3)
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<string>
 6 #include<cstring>
 7 #include<cstdlib>
 8 #include<algorithm>
 9 #include<bitset>
10 #define ms(a) memset(a,0,sizeof(a))
11 using namespace std;
12 const int MAXN=33;
13 const int MAXM=100002;
14 int head[MAXN*10000],w1[MAXN*10000],w[MAXN*10000],v[MAXN*10000],nxt[MAXN*10000],trie[MAXN*MAXM][2];
15 int xx,yy,zz,ans=0,tot=0,cnt=0;
16 bool vis[MAXN*10000];
17 int n,m,k;
18 void add(int x,int y,int z)
19 {
20     v[++tot]=y;w[tot]=z;
21     nxt[tot]=head[x];
22     head[x]=tot;
23 }
24 void dfs(int x)
25 {
26     for(int i=head[x];i!=-1;i=nxt[i]){
27         int y=v[i];
28         if(!vis[y]){
29             vis[y]=true;
30             w1[y]= w[i] xor w1[x];
31             dfs(y);
32         }
33     }
34 }
35 void insert(int x)
36 {
37     int now=0;
38     for(int i=30;i>=0;i--){
39         int temp=x&(1<<i)?1:0;
40         if(!trie[now][temp]){
41             trie[now][temp]=++cnt;
42         }
43         now=trie[now][temp];
44     }
45 }
46 int find(int x)
47 {
48     int now=0,w=0;
49     for(int i=30;i>=0;i--){
50         int temp=x&(1<<i)?1:0;
51         if(trie[now][!temp]){
52             w|=(1<<i);now=trie[now][!temp];
53         }
54         else now=trie[now][temp];
55     }
56     return w;
57 }
58 int main()
59 {
60     while(~scanf("%d",&n)){
61         ms(vis);ms(trie);ms(w);ms(w1);
62         memset(head,-1,sizeof(head));tot=ans=cnt=0;
63         int x,y,z;
64         for(int i=1;i<n;i++){
65             scanf("%d%d%d",&x,&y,&z);
66             add(x,y,z);add(y,x,z);
67         }
68         dfs(x);
69         for(int i=0;i<n;i++){
70             insert(w1[i]);
71             ans=max(ans,find(w1[i]));
72         }
73         cout<<ans<<endl;
74     }
75     return 0;
76 }
trie

 

posted @ 2018-05-28 23:01  杜宇一声  阅读(890)  评论(0编辑  收藏  举报