bzoj 5043 密码破译 dp

Description

 小Q发明了一个新的加密算法,对于一个长度为n的非负整数序列a_1,a_2,...,a_n,他会随机选择一个非负整数k,

将每个数都异或上k得到b_1,b_2,...,b_n,即b_i=a_i xor k。不幸的是,健忘的小Q睡了一觉之后就把密钥k忘得
一干二净了,不过他隐约记得a_1+a_2+...+a_n的值为m,你能帮他找到一个可行的密钥吗

Input

第一行包含两个整数n,m(1<=n<=100000,0<=m<2^{60}),分别表示序列的长度以及加密前所有数的和。
第二行包含n个整数b_1,b_2,...,b_n(0<=b_i<2^{60}),表示加密后的序列。

Output

 输出一个非负整数k,若无解输出-1,若有多组解,输出最小的k。

Sample Input

3 5
1 2 3

Sample Output

1


c[i]代表m的二进制第i位是否为1,d[i]代表数组b中二进制第i位有几个1,f[i][j]代表到第i个数,还剩j*(2^i)时的最小值。
//
//  main.cpp
//  bzoj5043
//
//  Created by zc on 2017/10/16.
//  Copyright © 2017年 zc. All rights reserved.
//

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long 
#define min(a,b) ((a)<(b))?(a):(b)
using namespace std;

const ll INF=1ll<<60;
const int N=2e5+10;

ll m, t, f[2][N];
int n, d[60], c[60] ,k;

int main(int argc, const char * argv[]) {
    scanf("%d%lld",&n,&m);
    for(int i=0;i<60;i++)   c[i]=(m>>i)&1;
    memset(d,0,sizeof(d));
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&t);
        for(int j=0;j<60;j++)   d[j]+=(t>>j)&1;
    }
    memset(f[0],127,sizeof(f[0]));
    f[0][0]=0;
    for(int i=59;i>=0;i--)
    {
        memset(f[i&1],127,sizeof(f[i&1]));
        for(int j=0;j<N;j++)
        {
            if(f[1-(i&1)][j]>=INF)  continue;
            k=j*2+c[i]-d[i];//第i位取0时
            if(k>=0&&k<N)   f[i&1][k]=min(f[i&1][k],f[1-(i&1)][j]);
            k=j*2+c[i]-n+d[i];//第i位取1时
            if(k>=0&&k<N)   f[i&1][k]=min(f[i&1][k],f[1-(i&1)][j]+(1ll<<i));
        }
    }
    if(f[0][0]>=INF)    f[0][0]=-1;
    printf("%lld\n",f[0][0]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值