[10.9模拟] big

题意:

你需要在([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;
}

原文地址:https://www.cnblogs.com/HLXZZ/p/7643387.html