牛客网-Beauty of Trees 【加权并查集】

锟斤拷锟接o拷https://www.nowcoder.com/acm/contest/119/A
锟斤拷源锟斤拷牛锟斤拷锟斤拷

锟斤拷目锟斤拷锟斤拷

It锟斤拷s universally acknowledged that there锟斤拷re innumerable trees in the campus of HUST.
One day the tree manager wants to play a game with you. There are N trees lining up in a straight road. The beauty of a set of trees is defined as the bitwise XOR sum of the heights of these trees. The game will last for M rounds, and each time he will tell you an interval [Li,Ri] and its beauty. However, he mixed some fake messages with the correct ones. Your task is to find the messages that cannot logically correspond to its former correct messages. Otherwise you锟斤拷ll think the message is correct.

锟斤拷锟斤拷锟斤拷锟斤拷:

The first line contains two integer N and M(1锟斤拷N,M锟斤拷10
5
), the number of trees and the rounds of game.
Then M lines followed, in each line are three integer L, R and k(1锟斤拷L锟斤拷R锟斤拷N,0锟斤拷k锟斤拷10
9
), indicating that the beauty of [L
i
,R
i
] is k.

锟斤拷锟斤拷锟斤拷锟�:

If the i-th message is wrong, then print i in a single line.
If there is no mistake, print -1.
示锟斤拷1

锟斤拷锟斤拷

3 4
1 3 6
1 2 5
3 3 10000
3 3 3

锟斤拷锟�

3

说锟斤拷

You can infer from the first two messages that the height of the third tree is 3.

r[a]记录的是a与其父亲节点的异或值
通过并查集合并的过程同时更新路径上的r值
令fa,fb分别是a,b,的父亲节点
我们知道r[a] = a^fa,r[b]=b^fa,a^b=c
那么在合并fa,fb的时候,令fb为fa的父节点
那么就有r[fa]=fa^fb
而r[a]=a^fa,所以fa=a^r[a]
同理fb=b^r[b]
所以r[fa] = r[a]^r[b]^a^b = r[a]^r[b]^c
当然对于一个节点合并问题不好解决,此时可以添加一一个新节点n作为根节点,使得n的值为0,那么任何值与0的异或都是本身
这样一来合并问题就解决了
 1 /**
 2 先要知道加权并查集:https://blog.csdn.net/qq_32944513/article/details/80218138
 3 然后 这里
 4 设 P[i] 为 0~i 的异或值, 则给了 (L,R)=k 相当就是知道了 P[r]^P[l-1]=k;
 5 这样转化一下 就和 LA4487 差不多了。
 6 **/
 7 #include<iostream>
 8 #include<stdio.h>
 9 #include<string.h>
10 #include<vector>
11 #include<map>
12 #include<algorithm>
13 using namespace std;
14 typedef long long int ll;
15 const int maxn = 100000;
16 const int inf = 1e9;
17 int n,m,fa[maxn+5],r[maxn+5],ans[maxn+5];
18 int findset(int x)
19 {
20     if(fa[x] == x) return x;
21     int fx = fa[x];
22     fa[x] = findset(fa[x]);
23     r[x]^=r[fx];
24     return fa[x];
25 }
26 int Merge(int u,int v,int z)
27 {
28     int fx = findset(u), fy = findset(v);
29     if(fx==fy)
30     {
31         return (r[u]^r[v])==z;
32     }
33     fa[fx] = fy;
34     r[fx]=r[u]^r[v]^z;
35     return true;
36 }
37 int main()
38 {
39     int num = 0;
40     scanf("%d %d",&n,&m);
41     for(int i=0;i<=n;i++) fa[i] = i,r[i] = 0;
42     for(int i=1; i<=m; i++)
43     {
44         int l,r,w;
45         scanf("%d %d %d",&l,&r,&w);
46         if(!Merge(l-1,r,w)) ans[++num] = i;
47     }
48     for(int i=1;i<=num;i++) printf("%d\n",ans[i]);
49     if(num==0) printf("-1\n");
50     return 0;
51 }
View Code

 

官方题解:

 1 /*
 2 考虑给定的区间[L,R]中的数和美丽值k的二进制分解。对于第j位,如果k在这一位是1,
 3 说明[L,R]之间在这一位为1的数有奇数个,接下来令f[i][j]表示第j位为1的数在前i个数里有多少个,
 4 那么这个条件也就是f[l-1][j]与f[r][[j]奇偶性不同;若k在这一位为0,
 5 说明f[l-1][j]与f[r][j]奇偶性相同。注意上述条件都是等价的,
 6 也就是说如果所有的j位f[l-1][j]和先前确定的与f[r][j]的关系不矛盾的话,
 7 就等价于题目所给的合乎逻辑。这个过程可以用并查集来完成。
 8 对于每一个前缀和开一个实点一个虚点,如果两个前缀和奇偶性相同,
 9 就实对实虚对虚相连;如果奇偶性不同,就各自实对虚相连。
10 这样就能保证与任意节点奇偶性偶数次不同相当于奇偶性相同。然后判断的时候可以直接查询。
11 */
12 #include<cstdio>
13 #include<cstring>
14 #include<iostream>
15 
16 using namespace std;
17 
18 const int maxn=1e5+10;
19 
20 int n,m,l,r,k,fl,cnt;
21 int f[maxn*2][32];
22 
23 int find(int t,int k)
24 {
25     int p=t,r;
26     while(t!=f[t][k])t=f[t][k];
27     while(p!=t)r=f[p][k],f[p][k]=t,p=r;
28     return t;
29 }
30 
31 void unio(int l,int r,int j)
32 {
33     if(find(l,j)!=find(r,j))
34         f[find(l,j)][j]=find(r,j);
35 }
36 
37 int main()
38 {
39     scanf("%d%d",&n,&m);
40     for(int i=0; i<=n*2+1; i++)
41         for(int j=0; j<=30; j++)
42             f[i][j]=i;
43 
44     for(int i=1; i<=m; i++)
45     {
46         fl=0;
47         scanf("%d%d%d",&l,&r,&k);
48         l--;
49 
50         for(int j=0; j<=30; j++)
51             if((k&(1LL<<j))!=0&&(find(l,j)==find(r,j)||find(l+n+1,j)==find(r+n+1,j)))
52             {
53                 printf("%d\n",i);
54                 fl=1;
55                 break;
56             }
57             else if((k&(1LL<<j))==0&&(find(l,j)==find(n+r+1,j)||find(r,j)==find(n+l+1,j)))
58             {
59                 printf("%d\n",i);
60                 fl=1;
61                 break;
62             }
63         if(fl)continue;
64 
65         cnt++;
66         for(int j=0; j<=30; j++)
67             if((k&(1LL<<j))!=0)unio(l,r+n+1,j),unio(r,l+n+1,j);
68             else unio(l,r,j),unio(l+n+1,r+n+1,j);
69     }
70 
71     if(cnt==m)printf("-1");
72 
73     return 0;
74 }
View Code

 

参考博客:戳这里

posted @ 2018-05-08 23:21  euzmin  阅读(299)  评论(0编辑  收藏  举报