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 }
View Code

 

  

posted @ 2013-08-24 17:06  pc....  阅读(966)  评论(0)    收藏  举报