UVALIVE 4487 Exclusive-OR(加权并查集)
2015-09-21 23:43
411 查看
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2488
题意:已知有n个数,但并不知道大小。有如下3种操作:
I a w:下标为u的数值为w。
I a b w:下标为u的数和下标为v的数的异或值为w。
Q k z1…zk:求下标为z1到zk的数的异或值。
思路:并查集,但并不是简单的并查集,在并查集所构成的树上的每条边都加了一个权值。我的做法如下:
f[i]:存储i的根结点标号。
v[i]:存储i与f[i]的异或值。
p[i]:存储i的值。
int find(int x):找x的根并做压缩的同时处理好每个节点与根的异或值。
void judge(int c, int r):若已知根r的值或已知孩子c的值,则求另外一个节点的值。
bool uni(int r, int &ans) :求每一个根结点为r的联通块的异或值,可求返回true,否则false。
首先对I操作来看:
若为操作1,则令 p[a] = w。
然后需要判断是否与之前的事实矛盾:用 find() 函数求 a 的根结点 r .
因为已知 p[a] 的值和 v[a] 的值,即理论上由异或的性质就可以求出 p[r] 的值。但若 p[r] 已经赋值,则需判断 w 与 p[r] 是否相同。
若为操作2,则用 find() 函数先求 a 的根 r1 和 b 的根 r2 。
若 r1 和 r2 不同根,则并在一起 f[r1] = r2,同时求出 v[r1] = w ^ v[a] ^ v[b],调用 judge() 函数。
若相同根,则只需判断是否合法,即 w 是否等于 v[a] ^ v[b]。
若为操作3,即询问。
先对询问中的节点构成的联通块进行合并(若2个不同联通块根的值都已知,则可以合并)。
然后对每个联通块求异或值(可以先求每个联通块的根)。
对于一个联通块,若节点数为奇数并且根的值未知,则无法求该联通块的异或值,否则可以求。
注意点:
对于一个联通块,若知道其中一个节点的值,则可以知道联通块中每个节点的值。
合并联通块的时候,需调用judge()函数,因为若一个联通块内的所有节点的值均知道,则合并后可知道另一个联通块内的所有节点的值。
输出don’t 的时候,单引号是英式的!!
代码:
(代码略长,但效率应该不低= =)
题意:已知有n个数,但并不知道大小。有如下3种操作:
I a w:下标为u的数值为w。
I a b w:下标为u的数和下标为v的数的异或值为w。
Q k z1…zk:求下标为z1到zk的数的异或值。
思路:并查集,但并不是简单的并查集,在并查集所构成的树上的每条边都加了一个权值。我的做法如下:
f[i]:存储i的根结点标号。
v[i]:存储i与f[i]的异或值。
p[i]:存储i的值。
int find(int x):找x的根并做压缩的同时处理好每个节点与根的异或值。
void judge(int c, int r):若已知根r的值或已知孩子c的值,则求另外一个节点的值。
bool uni(int r, int &ans) :求每一个根结点为r的联通块的异或值,可求返回true,否则false。
首先对I操作来看:
若为操作1,则令 p[a] = w。
然后需要判断是否与之前的事实矛盾:用 find() 函数求 a 的根结点 r .
因为已知 p[a] 的值和 v[a] 的值,即理论上由异或的性质就可以求出 p[r] 的值。但若 p[r] 已经赋值,则需判断 w 与 p[r] 是否相同。
若为操作2,则用 find() 函数先求 a 的根 r1 和 b 的根 r2 。
若 r1 和 r2 不同根,则并在一起 f[r1] = r2,同时求出 v[r1] = w ^ v[a] ^ v[b],调用 judge() 函数。
若相同根,则只需判断是否合法,即 w 是否等于 v[a] ^ v[b]。
若为操作3,即询问。
先对询问中的节点构成的联通块进行合并(若2个不同联通块根的值都已知,则可以合并)。
然后对每个联通块求异或值(可以先求每个联通块的根)。
对于一个联通块,若节点数为奇数并且根的值未知,则无法求该联通块的异或值,否则可以求。
注意点:
对于一个联通块,若知道其中一个节点的值,则可以知道联通块中每个节点的值。
合并联通块的时候,需调用judge()函数,因为若一个联通块内的所有节点的值均知道,则合并后可知道另一个联通块内的所有节点的值。
输出don’t 的时候,单引号是英式的!!
代码:
(代码略长,但效率应该不低= =)
#include <stdio.h> #include <iostream> #include <math.h> #include <algorithm> #include <string.h> using namespace std; #define LSON l, m, rt << 1 #define RSON m + 1, r, rt << 1 | 1 const int N = 2e4 + 10; const int M = 1e3 + 10; const int INF = (1 << 20) + 10; int num, n, err, ifacts; int a[M]; int f ; int v ; int p ; char str[M]; void init(char *s) { int len = strlen(s); s[len] = ' '; s[++len] = '\0'; num = 0; int t = 0; for (int i = 2; i < len; i++) { if (s[i] != ' ') { t = t * 10 + s[i] - '0'; } else { a[num++] = t; t = 0; } } } void judge(int c, int r) {//若已知根的值或已知孩子值,则求另外一个节点的值 if (p[c] != INF) {//孩子值已知 if (p[r] == INF)//根值未知 p[r] = (v[c] ^ p[c]); else if (p[r] != (v[c] ^ p[c]))//根值已知,判断是否是否合法 err = ifacts; } else if (p[r] != INF) {//孩子值未知,根值已知 p[c] = (p[r] ^ v[c]); } } int find(int x) {//找根的同时,计算每个节点和根的异或值,存入v数组 if (f[x] != x) { int r = f[x]; f[x] = find(f[x]); v[x] = v[r] ^ v[x]; judge(x, f[x]); } return f[x]; } bool uni(int r, int &ans) {//求每一个联通块的异或值,可求返回true,否则false int rv = p[r]; int tmp = 0, cnt = 0; for (int i = 1; i < num; i++) { if (f[a[i]] == r) { tmp ^= v[a[i]]; cnt++; } } if (cnt % 2 == 0) { ans ^= tmp; return true; } else {//奇数个节点,需判断根的值是否知道 if (rv == INF) return false; else ans ^= (tmp ^ rv); return true; } } int main() { int q, i_case = 1; while (scanf("%d%d", &n, &q) != EOF && n && q) { ifacts = 0; err = -1; for (int i = 0; i < N; i++) { f[i] = i; v[i] = 0; p[i] = INF; } getchar(); printf("Case %d:\n", i_case++); for (int i = 0; i < q; i++) { gets(str); if (err != -1) continue; init(str); if (str[0] == 'I') { ifacts++; if (num == 3) {//给出2个节点的异或值 int r1 = find(a[0]); int r2 = find(a[1]); if (r1 != r2) {//若不同根 f[r1] = r2; v[r1] = (a[2] ^ v[a[0]] ^ v[a[1]]); judge(r1, r2); } else if (a[2] != (v[a[0]] ^ v[a[1]])) { err = ifacts; printf("The first %d facts are conflicting.\n", ifacts); } } else {//给出1个节点的值 int r = find(a[0]); if (p[a[0]] != INF && p[a[0]] != a[1]) { err = ifacts; printf("The first %d facts are conflicting.\n", ifacts); } else p[a[0]] = a[1]; judge(a[0], r); } } else { if (num >= 2) { int r = find(a[1]); for (int i = 1; i < num; i++) {//合并可以合并的联通块,即若2个联通块的根的值都已知,则可以合并 int tr = find(a[i]); if (tr != r && p!= INF && p[r] != INF) { f = r; v = (p ^ p[r]); judge(tr, r); } } int root[20], rn = 0; root[rn++] = find(a[1]); for (int i = 2; i < num; i++) {//求出每个联通块的根 int r = find(a[i]); bool hav = false; for (int j = 0; j < rn; j++) if (root[j] == r) { hav = true; break; } if (!hav) root[rn++] = r; } bool dont = false; int ans = 0; for (int i = 0; i < rn; i++) { if (!uni(root[i], ans)) {//对于每个联通块求异或值,若某个联通块无法求值,则don't know dont = true; break; } } if (dont) printf("I don't know.\n"); else printf("%d\n", ans); } } } printf("\n"); } return 0; } 相关文章推荐
- QtQuick学习笔记(二)QML基本语法
- Qt安装与入门
- 什么是Servlet以及Servlet和JSP的关系是什么?
- WebView加载html实现网页上传本地文件(图片,拍照,语音等)
- Ret2Libc 练习(1) -- ZwSetInformationProcess
- Activity启动模式图文详解:standard, singleTop, singleTask 以及 singleInstance
- apue3 搭建(基于ubuntu 12.04LTS)
- Linux下QT中执行shell命令
- 2D 图像算法
- JSON格式序列化与反序列化(List、XML)
- c++课程后的感想与记录
- 菜鸟路之hibernate
- 【算法题目】包含min函数的栈
- 京东刘强东:中国互联网产业是巨大的泡沫
- 京东刘强东:中国互联网产业是巨大的泡沫
- 文件输出流
- 手机游戏
- Objective-C数据类型输出格式及隐式转换详解
- 关于web.xml的格式
- Quarz 配置