题意:
你需要在([0,2^n))中选一个整数 x,接着把 x 依次异或 m 个整数([a_1,a_m])
在你选出 x 后,你的对手需要选择恰好一个时刻(刚选完数时、异或一些数后或是最后),将 x 变为(( 2x/2^n+2x) pmod {2^n})
你想使 x 最后尽量大,而你的对手会使 x 最后尽量小
你需要求出 x 最后的最大值,以及得到最大值的初值数量
题解:
trie树+贪心
可以发现那个操作就是将二进制位上的数往前转一格,而由于异或每一位上的独立性,在每个时刻对手进行操作时,就相当于前面所有的数往前转一格再异或后面所有的数,即异或一个数
所以我们可以O(m)的枚举断点,然后把每一个要异或的那个数加入到trie树中去
最后在trie树上dfs一遍,如果当前位有0,1两个儿子,那么对手就一定可以把这一位搞成0,否则这一位一定能被你搞成1,于是你就可以得出那些位能产生贡献(即一定能选1),然后统计最大值,和达到最大值的方案即可
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define M 100010
using namespace std;
int n,m,sum,bin,id,tot,cnt;
int ch[M*30][2],a[M],b[M],suf[M],t[50],ans[M];//ch数组大小为mlog,至多有m条链,每条链上至多n个结点
int gi() {
int x=0,o=1; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') o=-1,ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return o*x;
}
void insert(int x) {
for(int i=0; i<n; i++) t[i]=((x>>(n-i-1))&1);
int u=0;
for(int i=0; i<n; i++) {
if(!ch[u][t[i]]) ch[u][t[i]]=++id;
u=ch[u][t[i]];
}
}
void dfs(int u, int now, int lev) {
if(lev<0) {
ans[++cnt]=now;return;
}
if(ch[u][0] && !ch[u][1]) dfs(ch[u][0],now^(1<<lev),lev-1);
if(ch[u][1] && !ch[u][0]) dfs(ch[u][1],now^(1<<lev),lev-1);
if(ch[u][0] && ch[u][1]) {
dfs(ch[u][0],now,lev-1);
dfs(ch[u][1],now,lev-1);
}
}
int main() {
n=gi(),m=gi(),bin=1<<n;
for(int i=1; i<=m; i++) a[i]=gi();
for(int i=m; i>=1; i--) suf[i]=suf[i+1]^a[i];
for(int i=0; i<=m; i++) {
sum^=a[i];
b[i]=((sum*2/bin)+2*sum)%bin^suf[i+1];
insert(b[i]);
}
dfs(0,0,n-1);
sort(ans,ans+cnt+1);
for(int i=cnt; i>=1; i--) {
if(ans[i]==ans[cnt]) tot++;
}
printf("%d
%d
", ans[cnt],tot);
return 0;
}