ZOJ 4086 Little Sub and a Game (一月月赛 F)

题意: 分别给定nn对数字对(Aix,Aiy)(A_i^x,A_i^y),和mm(Bix,Biy)(B_i^x,B_i^y)。有两个人,第一个人AA从每对(Aix,Aiy)(A_i^x,A_i^y)中选一个数字取异或和,即Axor=A0xyA1xyAnxyA_{xor}=A_0^{x|y} \otimes A_1^{x|y} \otimes \dots \otimes A_n^{x|y},另一个人BB从每对(Bix,Biy)(B_i^x,B_i^y)中选取一个数字取异或和,即Bxor=B0xyB1xyBmxyB_{xor}=B_0^{x|y} \otimes B_1^{x|y} \otimes \dots \otimes B_m^{x|y},最后得到一个数字ans=AxorBxorans=A_{xor}\otimes B_{xor}AA希望ansans尽可能大,BB希望ansans尽可能小。双方选择均为最优,ans值应该是多少。

分析: 因为所有的数字对对A,BA,B都是透明的,所以ansans值是唯一的。

  1. 首先这个问题并不是一个朴素的线性基问题。我们需要通过某种转换方法之后,使得它能使用线性基解决。首先构造Axor=A0xA1xAnxA_{xor}^{'}=A_0^x\otimes A_1^x\otimes \dots \otimes A_n^x。我们先将假设每次都取了AixA_i^x,然后通过判断是否要异或上Ai=AixAiyA_i^{'}=A_i^x\otimes A_i^y来选择是选择AixA_i^x还是AiyA_i^y。如,AxorAi=A0xAiyAnxA_{xor}^{'}\otimes A_i^{'}=A_0^{x}\otimes \dots \otimes A_i^{y} \otimes \dots \otimes A_n^{x}。那么我们可以对集合{A0,A1,,An}\{ A_0^{'},A_1^{'},\dots,A_n^{'}\}构造线性基PA={PA0,PA1,,PA63}\boldsymbol{PA}=\{PA_0,PA_1,\dots,PA_{63}\}.其中,PAiPA_i为最高位在ii上的基。同理可以对{B0,,Bm}\{B_0^{'},\dots,B_m^{'}\}构造出线性基PB={PB0,PB1,,PB63}\boldsymbol{PB}=\{PB_0,PB_1,\dots,PB_{63}\}.

  2. 我们首先得到一个ans=AxorBxorans'=A_{xor}^{'}\otimes B_{xor}^{'}。 接下来要做的是根据PA,PB\boldsymbol{PA,PB}来使得ansans'满足题目要求。因为线性基的性质,我们从高位开始判断。我们用ans[i]ans'[i]表示ansans'在第ii位上是0,10,1。那么对于第ii位来说,存在以下8中情况。用(ans[i],PAi,PBi)(ans'[i],PA_i,PB_i)来表示。其中,如果PAiPA_i不为0,表示PAiPA_i能够控制当前位置上是00还是11.PBiPB_i同理。

    • (0,0,0)(0,0,0)PAi,PBiPA_i,PB_i均没有控制能力,跳过。
    • (0,0,1)(0,0,1)PBiPB_i有控制能力,因为BB希望ansans'越小越好,且当前ans[i]ans'[i]已经是00,所以PBiPB_i不会修改这个位置上的值。
    • (0,1,0)(0,1,0)PAiPA_i有控制能力,因为AA希望ansans'越大越好,且当前ans[i]ans'[i]00,所以PAiPA_i会这个位置上的0变成1,即ans=ansPAians'=ans'\otimes PA_i
    • (0,1,1)(0,1,1),均有控制能力,稍后分析。
    • (1,0,0)(1,0,0)PAi,PBiPA_i,PB_i均没有控制能力,跳过。
    • (1,0,1)(1,0,1)PBiPB_i有控制能力,因为BB希望ansans'越小越好,且当前ans[i]ans'[i]已经是11,所以PBiPB_i会修改这个位置上的值,即ans=ansPBians'=ans'\otimes PB_i
    • (1,1,0)(1,1,0)PAiPA_i有控制能力,因为AA希望ansans'越大越好,且当前ans[i]ans'[i]11,所以PAiPA_i不会修改当前位置。
    • (1,1,1)(1,1,1),均有控制能力,稍后分析。
  3. 除了4,84,8两种情况,其他情况均很显然。重点是如何处理4,84,8这两种情况。对于第44种情况,无论我们是否要将PAiPA_i异或到ansans'上,PBiPB_i均要采用同样的操作。所以我们可以发现,对于当前这一位我们要么异或上PAi,PBiPA_i,PB_i,要么两个都不异或。我们发现这个时候PAiPA_i实际上并没有被使用。我们把PAiPA_i从线性基删去。然后构造一个新的变量PABi=PAiPBiPAB_{i}=PA_i\otimes PB_i,将PABiPAB_{i}插入线性基PA\boldsymbol{PA}。这样,如果选取了PABiPAB_{i},那么说明AAPAiPA_i异或上ansans',那么PBiPB_i也要异或上,反之则均不取。

  4. 第8种情况是4的特例,我们只需要先将ans=ansPAians'=ans'\otimes PA_i,那么情况(1,1,1)(1,1,1)就变成了(0,1,1)(0,1,1).

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cctype>
#include <string>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <fstream>
#include <set>
#include <ctime>
#include <queue>
#include <cstdlib>
#include <fstream>
#define sp system("pause")
#define lc(x) (x<<1)
#define rc(x) (x<<1|1)

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll>pll;

const int MAXN = 1e7;

using namespace std;
ll ans = 0;
int n, m;
ll pa[100], pb[100];
vector<pll>va, vb;
vector<ll>v;
int up = 63;
void getxor(vector<ll> &v, ll p[])
{
  for (int i = 0; i <= up; i++)p[i] = 0;
  for (auto &i : v)
  {
    for (int j = up; j >= 0; j--)
    {
      if ((1LL << j)&i)
      {
        if (!p[j]) { p[j] = i; break; }
        else i ^= p[j];
      }
    }
  }
}

void insertxor(ll i, ll p[])
{
  for (int j = up; j >= 0; j--)
  {
    if ((1LL << j)&i)
    {
      if (!p[j]) { p[j] = i; break; }
      else i ^= p[j];
    }
  }
}

int main()
{
  int T;
  cin >> T;
  while (T--)
  {
    ans = 0;
    scanf("%d%d", &n, &m);
    v.clear();
    for (int i = 0; i < n; i++)
    {
      ll x, y;
      scanf("%lld%lld", &x, &y);
      ans ^= x;
      v.push_back(x^y);
    }
    getxor(v, pa);
    v.clear();
    for (int i = 0; i < m; i++)
    {
      ll x, y;
      scanf("%lld%lld", &x, &y);
      ans ^= x;
      v.push_back(x^y);
    }
    getxor(v, pb);
    for (int i = up; i >= 0; i--)
    {
      if (pa[i] == 0 && pb[i] == 0)continue;
      if (pa[i] == 0 && pb[i] != 0)
      {
        if ((ans & (1LL << i)))ans ^= pb[i];
      }
      if (pa[i] != 0 && pb[i] == 0)
      {
        if (!(ans & (1LL << i)))ans ^= pa[i];
      }
      if (pa[i] != 0 && pb[i] != 0)
      {
        if ((ans&(1LL << i)))ans ^= pa[i];
        //pa[i] = 0;
        insertxor(pa[i] ^ pb[i], pa);
      }
    }
    printf("%lld\n", ans);
  }
}

/*
2 3
1 5
4 6
2 3
4 8
6 4
*/
©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值