本文主要是介绍uvalive4487 带权并查集,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
两种操作,I p q v表示p^q = v,如果与之前有冲突, 则输出“The first i facts are conflicting.”其中i为之前所有的I操作的次数(算上当前冲突这次)。Q k p1p2..pk表示求p1^p2...^pk的值,输出值或“I don't know.”
首先,I操作后面跟的参数个数不确定所以用if(sscanf(s, "%d%d%d", &p, &q, &v) == 2)来判断参数的个数。
再有,用d[i]表示i与其父节点的异或值,输入p q的时候就利用并查集来更新d。如果只输入p时怎么办呢?不妨建立一个虚节点n,如果输入的是一个节点的值,就让它的父节点指向n,这样d[p]就是p与n的异或了(n初始为0)。输入的时候判断是否有冲突,如果没有的话,就合并,n总是要做为父节点的(如果有的话)。
然后是询问部分。假如询问发生在一个集合中,包含k个点,如果k为偶数,那么直接把k个d[]异或起来就是答案;如果k是奇数,直接异或会多一个根的值。理由:经过find(i)之后,d[i]表示i与该集合根节点的异或值,那么偶数次异或同一个值就相当于什么都没有发生;奇数次呢就会多异或了一个根节点的值,而我们一定不知道根结点的值,除非根节点为n,因为所有已知值的节点都是n的子结点啊。理解这个,就可以判断并求值了。
最后把所有d[pi]异或在一起就是答案,根节点每出现过一次就标记一下(我习惯用vis数组来标记),判断一下是否有出现过奇数次的,且是不是n。记得把数组清零,我那样写是因为k不超过15,循环也不费时间,但把整个vis数组都循环赋值一遍的话,还是要消耗一点时间的。
关键理解:一个值被异或偶数次,就等于没异或。那么每个节点保存的是它与父节点的异或值,当你把每个节点的值异或起来时,如果它们中有父节点被异或了奇数次,那么这时你就多异或了一个不要异或的值。如果这个父节点是虚结点n的话,那没关系因为n的d值是0,异或它等于本身。但是如果不是n那么就判断不出。因为这个多异或的父节点值不知道它的d值是多少。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
///LOOP
#define REP(i, n) for(int i = 0; i < n; i++)
#define FF(i, a, b) for(int i = a; i < b; i++)
#define FFF(i, a, b) for(int i = a; i <= b; i++)
#define FD(i, a, b) for(int i = a - 1; i >= b; i--)
#define FDD(i, a, b) for(int i = a; i >= b; i--)
///INPUT
#define RI(n) scanf("%d", &n)
#define RII(n, m) scanf("%d%d", &n, &m)
#define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k)
#define RIV(n, m, k, p) scanf("%d%d%d%d", &n, &m, &k, &p)
#define RV(n, m, k, p, q) scanf("%d%d%d%d%d", &n, &m, &k, &p, &q)
#define RFI(n) scanf("%lf", &n)
#define RFII(n, m) scanf("%lf%lf", &n, &m)
#define RFIII(n, m, k) scanf("%lf%lf%lf", &n, &m, &k)
#define RFIV(n, m, k, p) scanf("%lf%lf%lf%lf", &n, &m, &k, &p)
#define RS(s) scanf("%s", s)
///OUTPUT
#define PN printf("\n")
#define PI(n) printf("%d\n", n)
#define PIS(n) printf("%d ", n)
#define PS(s) printf("%s\n", s)
#define PSS(s) printf("%s ", n)
///OTHER
#define PB(x) push_back(x)
#define CLR(a, b) memset(a, b, sizeof(a))
#define CPY(a, b) memcpy(a, b, sizeof(b))
#define display(A, n, m) {REP(i, n){REP(j, m)PIS(A[i][j]);PN;}}using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int MOD = 100000000;
const int INFI = 1e9 * 2;
const LL LINFI = 1e17;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int N = 22222;
const int M = 22;
const int move[8][2] = {0, 1, 0, -1, 1, 0, -1, 0, 1, 1, 1, -1, -1, 1, -1, -1};int n;
bool vis[N];
char op[2], s[M];
int f[N], d[N], a[M];int find(int x)
{if(x == f[x])return x;int tmp = f[x];f[x] = find(f[x]);d[x] ^= d[tmp];return f[x];
}bool add(int p, int q, int v)
{int x = find(p);int y = find(q);if(x == y)return v == (d[p] ^ d[q]);if(x == n)swap(x, y);f[x] = y;d[x] = d[p] ^ d[q] ^ v;return 1;
}int main()
{//freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);int m, p, q, v, k, fact, go, cas = 1;while(RII(n, m), n + m){REP(i, n + 1)f[i] = i;CLR(vis, 0);CLR(d, 0);fact = 0;go = 1;printf("Case %d:\n", cas++);while(m--){RS(op);if(!go){gets(s);continue;}if(op[0] == 'I'){fact++;gets(s);if(sscanf(s, "%d%d%d", &p, &q, &v) == 2)v = q, q = n;if(!add(p, q, v)){printf("The first %d facts are conflicting.\n", fact);go = 0;}}else{RI(k);v = 0;REP(i, k){RI(a[i]);p = find(a[i]);v ^= d[a[i]];a[i] = p;vis[p] = !vis[p];}q = 1;REP(i, k)if(vis[a[i]]){if(a[i] != n)q = 0;vis[a[i]] = 0;}if(q)PI(v);else puts("I don't know.");}}puts("");}return 0;
}
这篇关于uvalive4487 带权并查集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!