• 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 }
  • 相关阅读:
    泛型理解及应用(二):使用泛型编写通用型Dao层
    泛型的理解及应用(一):泛型擦除
    Servlet、Filter 生命周期
    Java多线程(六) 线程系列总结
    Java多线程(五) Lock接口,ReentranctLock,ReentrantReadWriteLock
    Java多线程(四) 线程池
    Java多线程(三) 多线程间的基本通信
    Java多线程(二) 多线程的锁机制
    Java多线程(一) 多线程的基本使用
    Spring Boot实战:模板引擎
  • 原文地址:https://www.cnblogs.com/Ash-ly/p/5397649.html
Copyright © 2020-2023  润新知