Beauty of Trees
题意:n个数,m个信息:L,R,k。每个信息表示a[L]^a[L+1]^a[L+2]^····^a[R]=k。如果第 i 个信息与前面 i-1 个信息矛盾的话就输出 i ,如果这m个信息都不矛盾的话,输出 -1
题解:位运算多半要考虑二进制,这里将k分解成二进制形式。先令dp[i][j]表示第i个数之前(包括第i个数),二进制第j位为1的数的个数。如果k的二进制的第j位为1的话,说明区间[L,R]中这一位为1的数有奇数个(区间的数考虑成二进制),那么dp[L-1][j]和dp[R][j]的奇偶性相同,为啥呢?因为奇数减奇数等于偶数,偶数减偶数等于偶数~~~~~。同理可以分析出如果k的二进制的第j位为0的话,说明区间[L,R]中这一位为1的数有偶数个,那么dp[L-1][R]和dp[R][j]的奇偶性不同。小技巧出现啦^_^,对每一位开一个虚拟节点,用并查集管理,如果dp[L-1][j]和dp[R][j]的奇偶性不同,那么实点和虚点相连。奇偶性相同,就实点和实点相连,虚点和虚点相连。显然判断是否矛盾的方式也就自然出来了:奇偶性相同,但在并查集中确是实点和虚点连在了一起,说明矛盾。-----------------参考华中校赛题解
#pragma warning(disable:4996) #include<cmath> #include<string> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define mem(arr,in) memset(arr,in,sizeof(arr)) using namespace std; const int maxn = 200010; int n, m; int pa[maxn][32]; int Find(int a, int j) { if (pa[a][j] == a) return a; return pa[a][j] = Find(pa[a][j], j); } void Union(int a, int b, int j) { int x = Find(a, j); int y = Find(b, j); if (x != y) pa[x][j] = y; return; } void Inite() { for (int i = 0; i <= 2 * n + 1; i++) { for (int j = 0; j <= 30; j++) { pa[i][j] = i; } } return; } int main() { while (cin >> n >> m) { Inite(); int cnt = 0; for (int i = 1; i <= m; i++) { int l, r, k; cin >> l >> r >> k; l--; bool flag = false; for (int j = 0; j <= 30; j++) { int tp = k & (1 << j); if(tp==0){ if (Find(l, j) == Find(r + n + 1, j) || Find(r, j) == Find(l + n + 1, j)){ printf("%d\n", i); flag = true; break; } } else{ if(Find(l, j) == Find(r, j) || Find(l + n + 1, j) == Find(r + n + 1, j)){ printf("%d\n", i); flag = true; break; } } } if (flag) continue; cnt++; for (int j = 0; j <= 30; j++) { int tp = k & (1 << j); if (tp == 0) { Union(l, r, j); Union(l + n + 1, r + n + 1, j); } else { Union(l, r + n + 1, j); Union(l + n + 1, r, j); } } } if (cnt == m) cout << "-1" << endl; } return 0; }