• UVALive(LA) 4487 Exclusive-OR(带权并查集)


    题意:对于n个数X[0]~X[n-1],但你不知道它们的值,通过逐步提供给你的信息,你的任务是根据这些信息回答问题,有三种信息如下:

    I p  v : Xp = v;    Xp 的值为v

    I p q v :  Xp ^ Xq = v;   Xp 异或Xq的值为v

    Q k X1 X2 X3 ..... Xk  : 问X1异或X2异或X3....异或Xk的值为多少?


    解题思路:先看处理部分(I),有两种 I 一种 是后面有两个整数的,一种是后面有三个整数的,(这里的输入要注意整数有可能是好几个字符)。先看三个整数的,Xp ^ Xq = v;那么我们可以利用并查集把 使 p 的父节点 为 q,再建立一个数组w[],代表Xp 异或 根节点的值,我们对于每次给出的两个数,p 和 q,把它们并到集合里面,利用路径压缩使得w[p]和w[q]都是和根节点异或的值,在压缩的时候维护这个数组就好。

    首先我们需要知道几个公式  a ^ b= c, 那么   a = b ^ c  b = a ^ c;

    异或运算有结合律,交换律。

    a ^ a = 0;

    a ^ 0 = a;

    对于 I p q v :   p ^ q = v;

    假设:

    fp  是  p 的根节点,那么 fp = p ^ w[p];     1

    fq  是  q 的根节点,那么 fq = q ^ w[q];     2

                                     p ^ q = v ;          3

    我们把 fp 的父节点设为 fq(新的根节点) 那么  w[fp] = fp ^ fq;   4

    把 1 2 3 式带到 4  式的  w[fp] = w[p] ^ w[q] ^ v;

    这样就解决了路径压缩过程中的维护数组问题。对于 I  后面仅有 二个整数的情况,I  p v   p = v;我们可以变形一下   p ^ 0 = v;这样是不是就和上面那种情况很像了呢,即令 q = 0,就好了,因为 我们的编号是  0 ~ N- 1,为了不和其他节点弄混,那么我们可以把这个节点用编号 n 来表示,只不过已知 n 的值为 0 就好了~~~~,然后处理数据这部分就解决了~~~~

    紧接着我们看询问部分(Q),询问的时候 假设 询问的是 X1,X2,X3..........Xn,

    我们先看 w[X1] ^ w[X2]^w[X3]^....w[Xn],假设他们的根节点是  R(他们在一个集合里面),

    那么w[X1] ^ w[X2]^w[X3]^....w[Xn]

       =  X1 ^R ^  X2 ^R ^ X3  ^ R ^ ......... ^ Xn ^ R

    这里一共有 N 个 R  ,由公式  a ^ a= 0;  a ^ 0 = a;而且异或具有交换律,那么 这里的 N 如果是 偶数 我们 就可以求得X1 ^R ^  X2 ^R ^ X3  ^ R ^ ......... ^ Xn ^ R = X1 ^ X2 ^ X3 ^ ......^Xn,如果是奇数那么最后就会剩下一个单独的R。。。这里这个 R 是已知的话那么也可以求出来 ,怎么会是已知的呢?我们可以想想有一个特殊的  ,就是那个编号为 N 的节点,他的值是 0 ,所以就要分情况了 

    if(R是N)

    {

       X1 ^ X2 ^ X3 ^ ......^Xn =w[X1] ^ w[X2] ^ w[X3] ^....w[Xn];

    }

    else

    {

       if(R的数量是奇数)

       {

             不知道值;

       }

      else

      {

            X1 ^ X2 ^ X3 ^ ......^Xn =w[X1] ^ w[X2] ^ w[X3] ^....w[Xn];

      }

    }

    这样整个过程就结束了。。。。。

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <cstdlib>
      5 #include <cmath>
      6 #include <cctype>
      7 #include <algorithm>
      8 #include <queue>
      9 #include <stack>
     10 #include <map>
     11 #include <set>
     12 using namespace std;
     13 
     14 const int MAXN = 2e4 + 3;
     15 int pre[MAXN];       //存放的是父节点
     16 int weight[MAXN];    //存放是与根节点的异或值
     17 int xp[MAXN];       //询问时的数
     18 int n;
     19 
     20 int Find(int x)       //带路径压缩的查找
     21 {
     22    // printf("1");
     23     if(x != pre[x])
     24     {
     25         int fx = pre[x];
     26         pre[x] = Find(pre[x]);
     27         weight[x] ^= weight[fx];        //维护数组
     28         //printf("2");
     29     }
     30     return pre[x];
     31 }
     32 
     33 int mix(int x, int y, int z)
     34 {
     35     int fx = Find(x);
     36     int fy = Find(y);
     37     //printf("x = %d y = %d z = %d fx = %d fy = %d
    ",x,y,z,fx,fy);
     38     if(fx  == fy)            //如果给出的两个数已经出现过。判断现在给出的这个 I  是不是和以前的冲突(矛盾)
     39     {
     40         if((weight[x] ^ weight[y]) != z)      //(出现了矛盾)
     41             return 0;
     42         return 1;
     43     }
     44     if(fx == n) fx ^= fy ^= fx ^= fy;      //为了使得 n 成为 根节点
     45     pre[fx] = fy;
     46     weight[fx] = weight[x] ^ weight[y] ^ z;    //fx 的父节点设为 fy(新的根节点)后维护weight[fx]的值
     47     return 1;
     48 }
     49 
     50 int solv(int key)
     51 {
     52     int index[MAXN]={0};
     53     int ans = 0;
     54     for(int i = 0; i < key ; i++)    //查找森林中的每个树,统计 根节点被异或的次数
     55     {
     56         if(index[i]) continue;
     57         int fi = Find(xp[i]);
     58         int cnt = 0;
     59         for(int j = i ; j < key; j++)
     60         {
     61             int fj = Find(xp[j]);
     62             if(index[j] == 0 && fi == fj)
     63             {
     64                 cnt++;
     65                 index[j] = 1;
     66                 ans ^= weight[xp[j]];
     67             }
     68         }
     69         if((cnt&1) && fi != n)     //根节点被异或的次数是奇数  而且不是那个特殊的根节点
     70             return -1;
     71     }
     72     return ans;
     73 }
     74 
     75 int main()
     76 {
     77     //freopen("in.cpp","r",stdin);
     78     int Q;
     79     int kas = 1;
     80     while(scanf("%d%d",&n,&Q) && (n || Q))
     81     {
     82         printf("Case %d:
    ",kas++);
     83         for(int i = 0; i <= MAXN; i++)   //初始化 开始每个点都是一棵树,自身异或自身是 0 ,
     84         {
     85             pre[i] = i;
     86             weight[i] = 0;
     87         }
     88         int facts = 0;
     89         int flag = 0;
     90         while(Q--)
     91         {
     92             char order[2]={0};
     93             scanf("%s",order);
     94             int p,q,v;
     95             if(order[0] == 'I')
     96             {
     97                 facts++;
     98                 getchar();
     99                 char str[7]={0};
    100                 gets(str);
    101                 int k = 0;
    102                 for(int i = 0; str[i] != ''; i++)
    103                     if(str[i] == ' ') k++;
    104                 if(k == 1)
    105                 {
    106                     sscanf(str,"%d%d",&p,&v);     //I  后面只有两个数
    107                     q = n;
    108                 }
    109                 else
    110                 {
    111                     sscanf(str,"%d%d%d",&p,&q,&v);//I  后面只有三个数
    112                 }
    113                 if(flag) continue;   //矛盾
    114                 if(mix(p,q,v) == 0) //矛盾了
    115                 {
    116                     printf("The first %d facts are conflicting.
    ",facts);
    117                     flag = 1;
    118                 }
    119             }
    120             else if(order[0] == 'Q')
    121             {
    122                 int k;
    123                 scanf("%d",&k);
    124                 memset(xp,0,sizeof(xp));
    125                 for(int i = 0; i < k; i++)
    126                 {
    127                     scanf("%d",&xp[i]);
    128                 }
    129                 if(flag) continue;//矛盾
    130                 int ans = solv(k);
    131                 if(ans == -1)
    132                 {
    133                     printf("I don't know.
    ");
    134                 }
    135                 else
    136                     printf("%d
    ",ans);
    137             }
    138         }
    139         printf("
    ");
    140     }
    141     return 0;
    142 }
  • 相关阅读:
    解决UITableView中Cell重用机制导致内容出错的方法总结
    Hdu 1052 Tian Ji -- The Horse Racing
    Hdu 1009 FatMouse' Trade
    hdu 2037 今年暑假不AC
    hdu 1559 最大子矩阵
    hdu 1004 Let the Balloon Rise
    Hdu 1214 圆桌会议
    Hdu 1081 To The Max
    Hdu 2845 Beans
    Hdu 2955 Robberies 0/1背包
  • 原文地址:https://www.cnblogs.com/Ash-ly/p/5397649.html
Copyright © 2020-2023  润新知