HDU 3234 Exclusive-OR 并查集
http://acm.hdu.edu.cn/showproblem.php?pid=3234
题意:
给出很多组关系(异或)(点的标号是从0到n-1),然后让你求指定的关系的答案,如果求不出则输出
I don't know.
若输入的关系和之前输入的关系有冲突,则输出
The first i facts are conflicting.
i 表示第几次输入,发生冲突时,后面就不需要继续操作了,只需要输入就行了。
坑爹:
神之并查集!!!!做了这题对并查集了解更深了。而且本题也用到了异或的特性,
a^b = 1 , b^c = 2 , 那么 a^c = 1^2 = 3
一开始想的是,只要2个点不在同一个树上时,我就将它们并进去,用一个数组存他与
父亲节点的异或值(不是根节点),如果给的是一个点的话,我就虚拟一个N号节点,
将他与根节点相连(保存根节点的数值),一个集合里只要知道了一个点的值就能知道这个
集合中每一个点的值。在find函数里面递归的时候就能将每个点压缩,所以可以算出每一个
点和根节点的异或值。但是当我一组关系都不知道的时候,只知道每个点的值,也就是说有很
多个集合,这样就会要虚拟很多个N节点。当我发现这个问题时,貌似已经很难改动代码了。。
解法:
只建立一个N点,如果这个集合的每一个值都能算出来的话才与N相连。
如果知道了4的值就变成
最后压缩成这样,就相当与知道了每一个值。
1 #include<iostream> 2 #include<algorithm> 3 #include<string> 4 using namespace std; 5 6 const int maxn = 40000 + 10; 7 const int INF = 0x3fffffff; 8 9 int n; 10 int Q; 11 int father[maxn]; 12 int Rank[maxn]; 13 int format[maxn][3]; 14 int Query[maxn]; 15 16 void init() 17 { 18 int i; 19 for(i=0; i<maxn; i++) 20 { 21 father[i] = i; 22 Rank[i] = 0; 23 } 24 } 25 26 void Swap(int &a,int &b) 27 { 28 int c; 29 c = a; 30 a = b; 31 b = c; 32 } 33 34 int find(int x) 35 { 36 if(x == father[x]) 37 { 38 return father[x]; 39 } 40 int y = find(father[x]); 41 Rank[x] ^= Rank[father[x]]; 42 return father[x] = y; 43 } 44 45 bool Union(int a,int b,int v) 46 { 47 int root_a = find(a); 48 int root_b = find(b); 49 if(root_a == root_b) 50 { 51 return (Rank[a]^Rank[b]) == v; 52 } 53 if(root_b == n) 54 { 55 Swap(root_a,root_b); 56 } 57 father[root_b] = root_a; 58 Rank[root_b] = Rank[a] ^ Rank[b] ^ v; 59 return true; 60 } 61 62 int Update() 63 { 64 int k; 65 cin>>k; 66 int i; 67 int num[maxn]; 68 int root[maxn]; 69 bool flag = false;; 70 for(i=0; i<k; i++) 71 { 72 cin>>num[i]; 73 root[i] = find(num[i]); 74 } 75 sort(root,root+k); 76 if(root[k-1] != n /* 77 集合可能有多个,只要知道集合中的一个数,就能知道集合中的每一个数,再将其连到 N(根) 节点上 78 等于 N 时表示 这个集合的单个数字是算不出来的 79 */ 80 && k % 2 == 1/* 81 某个集合中要计算的数字为奇数个时 , ^运算的时候会抵消掉中间的数 82 例子: 已知 a^b , b^c , c^a 。 求a^b^c(奇数个) 不管怎么样都会抵消掉。 83 */ ) 84 { 85 flag = true; 86 } 87 for(i=1; i<k; i++) 88 { 89 if(root[i] != root[i-1] && i % 2 == 1/* 90 集合内的个数要成对出现才能计算 , 也即是上面 k % 2 == 1为什么不能计算的原因 91 */) 92 { 93 flag = true; 94 break; 95 } 96 } 97 if(flag) 98 { 99 return -1; 100 } 101 else 102 { 103 int sum = 0; 104 for(i=0; i<k; i++) 105 { 106 sum ^= Rank[num[i]]; 107 } 108 return sum; 109 } 110 } 111 112 int main() 113 { 114 //freopen("out.txt","w",stdout); 115 int Case = 1; 116 while(cin>>n>>Q,n+Q) 117 { 118 init(); 119 cout<<"Case "<<Case++<<":"<<endl; 120 int i; 121 int insertNum = 0; 122 bool flag = false; 123 for(i=0; i<Q; i++) 124 { 125 string str; 126 cin>>str; 127 if(str[0] == 'I') 128 { 129 int j = 0; 130 insertNum ++; 131 while(cin>>format[i][j++]) 132 { 133 if(getchar() == '\n') 134 { 135 break; 136 } 137 } 138 if(flag) 139 { 140 continue; 141 } 142 if(j == 3) 143 { 144 if(!Union(format[i][0],format[i][1],format[i][2])) 145 { 146 cout<<"The first "<<insertNum<<" facts are conflicting."<<endl; 147 flag = true; 148 } 149 } 150 else 151 { 152 if(!Union(format[i][0], n, format[i][1])) 153 { 154 cout<<"The first "<<insertNum<<" facts are conflicting."<<endl; 155 flag = true; 156 } 157 } 158 } 159 else 160 { 161 int ans = Update(); 162 if(flag) 163 { 164 continue; 165 } 166 else 167 { 168 if(ans != -1) 169 { 170 cout<<ans<<endl; 171 } 172 else 173 { 174 cout<<"I don't know."<<endl; 175 } 176 } 177 } 178 } 179 cout<<endl; 180 } 181 return 0; 182 }
浙公网安备 33010602011771号