uvalive4487 带权并查集

2024-06-16 01:32
文章标签 查集 带权 uvalive4487

本文主要是介绍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 带权并查集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065136

相关文章

poj 1182 并查集 食物链类

题意: 有n只动物,分别编号1....n。所有动物都属于A,B,C中的一种,已知A吃B,B吃C,C吃A。 按顺序给出下面两种共K条信息: 1. x 和 y 属于同一类。 2. x 吃 y 。 然而这些信息可能会出错,有可能有的信息和之前给出的信息矛盾,也有的信息可能给出的 x 和 y 不在n的范围内。 求k条信息中有多少条是不正确的。 解析: 对于每只动物,创建3个元素 i

POJ1988带权并查集

M u v 将u所在的堆移动到v所在的堆的上面 C u 求u的下面有多少块 带权并查集 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;i

POJ1703带权并查集

D: u v  u与v在不同的集合 A: u v  查询u与v的关系 1)压缩路径过程        fu->root   0  1  u-fu 0                 0  1   1                 1  0 2)合并过程 fu->fv      u->fu   0 1   v->fv 0            1 0 1            0

并查集基础与简单扩展应用

并查集 基础题目路径压缩 扩展应用扩展题目1扩展题目2 并查集的结构是一棵树 并查集有两种功能,一种是判断两个元素是否在同一集合,第二种是合并两个集合 并查集的实现需要记录每个节点的父亲节点 判断两个元素是否在同一集合,即判断两个元素的祖宗节点是否是一个节点(祖宗代表整棵树的根节点) 合并两个集合,即将任意一个集合祖宗的爸爸改为另一个集合的祖宗 基础题目 一共有

nyoj99(并查集+欧拉路+dfs)

单词拼接 时间限制: 3000 ms  |  内存限制: 65535 KB 难度: 5 描述 给你一些单词,请你判断能否把它们首尾串起来串成一串。 前一个单词的结尾应该与下一个单词的道字母相同。 如 aloha dog arachnid gopher tiger rat   可以拼接成:aloha.arachnid.dog.gopher.rat.tiger 输入 第一行是一个整

nyoj42(并查集解决欧拉回路)

一笔画问题 时间限制: 3000 ms  |  内存限制: 65535 KB 难度: 4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来。 规定,所有的边都只能画一次,不能重复画。   输入 第一行只有一个正整数N(N<=10)表示测试数据的组数。 每组测试数据的第一行有两个正整数P,Q(P<=1000,Q<

【HDU】5222 Exploration(并查集+拓扑排序)

对于无向边使用并查集合并成一个集合,对于有向边使用判断是否存在环 唯一无语的地方就是看输入是百万级的,加个输入挂跑9s,不加挂跑了5s #include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;#pragma comment(linker, "/STACK:102

【HDU】How Many Answers Are Wrong(带权并查集)

num[i]代表i到根节点的值 这道题一开始竟然以为是线段树= =!后来发现线段树无法进行子区间的维护 #include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 222222;int fa[maxn],num[maxn];int find_father(int

2014级寒假特训之并查集专题

Problem A: Double和XXZ的生日宴请 Time Limit: 1 Sec   Memory Limit: 128 MB Submit: 9   Solved: 7 [ Submit][ Status][ Web Board] [ Edit] [ TestData] Description Double 和 XXZ同一天生日,他们俩30岁生日那天,当年

【POJ】1733 Parity game 并查集

传送门:【POJ】1733 Parity game 题目大意:给你一个长度为n的01序列,再给你m句话,每句话是一个区间【L,R】,告诉你区间【L,R】中1的个数,现在你的任务是找到从第几句话开始说的和前面矛盾,出现第一次假话的时候前面有多少是真话。 题目分析:一开始看几乎没思路啊。后来没办法了,只能跑别人的博客去看看了。。。一看到说把一个区间【L,R】拆成两个区间【0,L-1】,