big (Trie)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值