10.4
思路:
每次操作相当于把前面几个数都在二进制意义下左移一位,然后与后面的所有数异或起来,再找x。
对手做的是将 x在二进制下左移一位,把超出二进制下n位的部分接到最后。而异或 i个数后左移等价于开始时先把前 i个数左移一位,再异或起来。
这是因为,这个运算只是二进制位上的位置变化,而对于按位异或时,二进制位置上的规律变化之后,不同数间的每个二进制位还是对的上的,而按位异或只要保证位置相对,位置上的交换其实没有关系。然后尽管x选取本身也会左移,但是鉴于我们并不关心x到底是什么,所以查找时就直接查找x左移之后的值操作后最小的最大值就行了 。
问题转化为选一个数,使它左移位后与 m+1 个给定数分别异或的最小值最大。
将 m+1 个数建立一棵字典树,从上到下遍历来最大化结果:走到一个点时,如往下只有0,说明我们这一位取1异或后只能是1,累计结果中加上这一位的值,只有 1也一 样;如果既有 0又有 1,说明这一位无论怎么取最后都是0, 分别往下走再比较即可 。
#include <cstdio>
#include <iostream>
using namespace std;
struct node{
int big, num, dep;
node *ch[2];
}pool[5000010], *tail = pool,*root;
int n,m,a[200010],b[200010];
node *newnode(int p){
node *nd = ++tail;
nd->dep = p;
nd->ch[0] = nd->ch[1] = 0;
return nd;
}
void insert(int x){
node *p = root;
for(int i=n-1; i>=0; i--){
int idx = ( (1<<i) & x ) > 0;
if( !p->ch[idx] )
p->ch[idx] = newnode(i);
p = p->ch[idx];
}
}
int wrk(int x){
return (x<<1) % (1<<n) + (x<<1) / (1<<n);
}//(2x/2^n + 2x) mod 2^n
void dfs(node *nd){
if(!nd->ch[1] && !nd->ch[0]){
nd->big = 0, nd->num = 1;
return ;
}
if(!nd->ch[1]){
dfs(nd->ch[0]);
nd->num = nd->ch[0]->num;
nd->big = nd->ch[0]->big + (1<<(nd->dep)-1);
}
else if(!nd->ch[0]){
dfs(nd->ch[1]);
nd->num = nd->ch[1]->num;
nd->big = nd->ch[1]->big + (1<<(nd->dep)-1);
}
else{
dfs(nd->ch[0]), dfs(nd->ch[1]);
if(nd->ch[0]->big > nd->ch[1]->big)
nd->big = nd->ch[0]->big, nd->num = nd->ch[0]->num;
else if(nd->ch[0]->big < nd->ch[1]->big)
nd->big = nd->ch[1]->big, nd->num = nd->ch[1]->num;
else nd->big = nd->ch[0]->big, nd->num = nd->ch[0]->num + nd->ch[1]->num;
}
}
int main(){
freopen ("big.in", "r", stdin);
freopen ("big.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=1; i<=m; i++)
scanf("%d", &a[i]), b[1] ^= a[i];
root = newnode(n);
for(int i=1; i<=m; i++)
b[i+1] = b[i] ^ a[i] ^ wrk(a[i]);
for(int i=1; i<=m+1; i++)
insert( b[i] );
dfs( root );
printf("%d\n%d\n", root->big, root->num);
return 0;
}