模拟赛2 T2 断电 字典树查询最大异或,子集标记询问最大与,或

文章目录

题意

1. . 2. x , , . 3. . 1.加入一个数.\newline2.询问x与加入的数中某一个的最大异或,最大与,最大或.\newline3.仅询问最大异或.

做法

求最大异或的做法不用多说了,加入字典树,贪心即可,仁慈的出题人给了20分部分分.
关键是求最大与,最大或. 这里用的方法叫做 子集标记法 (我自己取的名字).
我们用一个 v i s [ 1 < < 20 ] vis[1<<20] 的数组标记 x x 子集是否出现过.
那么当我们加入一个全集 S S 的时候,我们随便去掉 S S 某一位上的 1 1 ,产生的就是它的一个子集.这时我们再递归将子集当做全集加入,就可以了.
0 2 20 0\to2^{20} 之间每一个数只会加入一次,均摊复杂度 O ( 2 20 ) O(2^{20}) .
考虑回答最大与和最大或的询问.
从高位开始,如果是与,贪心寻找 x x 的最高位,如果有,就直接将这一位加入答案.
没有这一位的数无论如何都不可能大过现在的答案,最高位必定要取.
现在 a n s ans 已经有了这一位,再往下寻找 x x 1 1 的位,看看 a n s + ans+这一位 有没有被标记过.
如果标记过,又是贪心选择这一位.
以此类推.
如果是或,反过来寻找 x x 0 0 的位置即可.
AC代码如下.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=1<<20;
typedef int fuko[yuzu<<5];
int sz,q;
 
struct trie{
fuko ch[2],cnt;
void insert(int x){
  int i,p,now=0;
  for (i=21;~i;--i){
    p=x>>i&1;
    if (!ch[p][now]) ch[p][now]=++sz;
    now=ch[p][now];
    }
  }
int query(int x){
  /*最大异或不用解释*/
  int i,p,now=0,llx=0;
  for (i=21;~i;--i){
    p=x>>i&1;
    if (ch[!p][now]) now=ch[!p][now],llx|=1<<i;
    else now=ch[p][now];
    }
  return llx;
  }
}my_;
 
struct and_or{
fuko vis;
void insert(int x){
  if (vis[x]) return; // 每个子集只加入一次.
  vis[x]=1;
  for (int i=19;~i;--i)
    if (x>>i&1) insert(1<<i^x); // 加入x的每一个子集
  }
int maxand(int x){
  int i,llx=0;
  for (i=19;~i;--i)
    if (x>>i&1&&vis[1<<i^llx]) // x这一位上为1 并且ans+这一位的值被标记过.
      llx^=1<<i;
  return llx;
  }
int maxor(int x){
  int i,llx=0;
  for (i=19;~i;--i)
    if (!(x>>i&1)&&vis[1<<i^llx]) // x这一位上为0, ans+这一位的值被标记过.
      llx^=1<<i;
  return llx|x; // 最后不要忘了加上x.
  }
}your;
 
int main(){
for (q=read();q--;){
  int i,op=read(),x=read();
  switch(op){
    case 1:
      /*一个my一个your是不是很有意思啊.*/
      my_.insert(x);
      your.insert(x);
      break;
    case 2:
      write(my_.query(x)),p32;
      write(your.maxand(x)),p32;
      write(your.maxor(x)),pl;
      break;
    case 3:
      write(my_.query(x)),pl;
      break;
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_31908675/article/details/82845732
T2