Google Kickstart 2019G The Equation

Problem

The laws of the universe can be represented by an array of N non-negative integers. The i-th of these integers is Ai.

The universe is good if there is a non-negative integer k such that the following equation is satisfied: (A1 xor k) + (A2 xor k) + ... (AN xor k) ≤ M, where xor denotes the bitwise exclusive or.

What is the largest value of k for which the universe is good?

Input

The first line of the input gives the number of test cases, TT test cases follow. Each test case begins with a line containing the two integers N and M, the number of integers in A and the limit on the equation, respectively.

The second line contains N integers, the i-th of which is Ai, the i-th integer in the array.

Output

For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the largest value of k for which the universe is good, or -1 if there is no such k.

Limits

Time limit: 15 seconds per test set.
Memory limit: 1GB.
1 ≤ T ≤ 100.
1 ≤ N ≤ 1000.

Test set 1 (Visible)

0 ≤ M ≤ 100.
0 ≤ Ai ≤ 100, for all i.

Test set 2 (Hidden)

0 ≤ M ≤ 1015.
0 ≤ Ai ≤ 1015, for all i.

 

Sample


Input
 

Output
 
4
3 27
8 2 4
4 45
30 0 4 11
1 0
100
6 2
5 5 1 5 1 0

  
Case #1: 12
Case #2: 14
Case #3: 100
Case #4: -1

  

In sample case #1, the array contains N = 3 integers and M = 27. The largest possible value of k that gives a good universe is 12 ((8 xor 12) + (2 xor 12) + (4 xor 12) = 26).

In sample case #2, the array contains N = 4 integers and M = 45. The largest possible value of k that gives a good universe is 14 ((30 xor 14) + (0 xor 14) + (4 xor 14) + (11 xor 14) = 45).

In sample case #3, the array contains N = 1 integer and M = 0. The largest possible value of k that gives a good universe is 100 (100 xor 100 = 0).

In sample case #4, there is no value of k that gives a good universe, so the answer is -1.

分析

题目大概意思是说给一个长度为N的数组,里面的数用Ai表示,再给一个M值,需要找到一个非负整数k使得如下式子成立:

(A1 xor k) + (A2 xor k) + ... (AN xor k) ≤ M。

比赛时的思路:对于二进制数来说,越高位是1数值越大,因此想到用贪心算法从可能的最高位求解,但是没想到如何通过事先的计算简化求解过程。

以下是官方给出的分析原文:

Test set 1 (Visible)

For the first test set, notice that the maximum value of k is 127. This is because each Ai is at most 100, so the leading digit of Ai is at most 26 = 64. If k ≥ 128, then the leading digit of k is at least 27 = 128, meaning that (Ai xor k) ≥ 128 > M.

Hence, we can compute the answer by checking each value of k less than 128 and finding the largest one which produces a sum less than M.

这一题的位置是第二题,可以先考虑小数据集,从小数据集的大小出发(分析题目中给出的取值范围很重要),规定了Ai<=100而且M<=100,那么k的最大值一定是127,因为2^6<10<2^7,所以Ai的二进制最高位一定是第7位,如果k第八位是1的话,和Ai异或的结果第八位也是1,那么就大于了128,也就大于M,因此k不能有第8位。那么我们可以从127开始逐个遍历,直到找到符合条件的k为止。

Test set 2 (Hidden)

注意这里的“可能的最高位”不一定是最大的Ai的最高位,如果M比最大的Ai大很多,则很有可能k也比最大的Ai大很多。所以对于k可能取的最高位,应该根据M来看,对于大数据集,M的最大值是10^15,2^10>10^3,所以2^50>10^15,所以k的最高位可能是第51位,那么可以从第51位开始遍历,依次看相应位的k能否是1。

For the second test set, the reasoning above tells us that k < 2^50, which is too big for us to check every value.

Instead, notice that each bit of k only affects a single bit of each Ai. We can use this property to compute each bit of k separately.

For each 1 ≤ i ≤ 50, define ones(i) to be the number of rules Ai with the i-th bit (numbered starting from the least significant bit) equal to 1. Likewise, define zeroes(i) to be the number of rules with the i-th bit equal to 0. Then we can re-write the sum:

as:

Note that we can minimize this sum by choosing the i-th bit of k to be 1 if ones(i) ≥ zeroes(i), or 0 otherwise. Define f(j) to be the minimum value of the above sum over all bits i ≤ j. We can use f(j) to determine if a feasible value of k exists for the lowest j bits, which lets us solve this problem greedily. The greedy solution is as follows: starting from the most significant bit i, check if we can set it to be one (by adding cost of setting this bit to one and f(i-1)). If this value is less than or equal to m, there exists a feasible k with the i-th bit set to one. Since we want to set to maximize k, it is optimal for us to set this bit to 1. Otherwise, if the sum is larger than m, set the bit to zero. Then iterate by decreasing m by the cost at the current bit and checking the next most significant bit (i-1). In this way, we are able to find the largest feasible k. If f(i) is precomputed, the runtime of this algorithm is O(Nlog(max(Ai))).

Note that since Ai ≤ 10^15 and N ≤ 1000, the maximum sum is a little more than 10^18, so using 64-bit integers is sufficient for this problem.

这里用到了k的一个特点,k的每一位只影响A的对应位,不对其他位产生影响。

我们的思路是:从第51位开始逐个遍历,看让K取1是否可行。其中用到的f(i)表示第i位之后异或和能取到的最小值,这是事先算出的。计算方法是:首先从第51位开始计算每一位含有的1和0的数量,分别保存在one[]和zero[]数组中,对于第i位,如果one[i]>zero[i],那么让k取1可以使异或和取到最小;反之如果zero[i]>one[i],让k取0可以使异或和最小。从第1位开始,计算f[i]的值,表示第i位及之前的位能取到的最小异或和。

代码

#include <bits/stdc++.h>
using namespace std;
class sel{
    public:
        int count(long long A[],int lenA,int maxsum){
            long long maxAi=*max_element(A,A+lenA);
            long long temp=maxAi/2;
            int count=1;//最大的数的二进制位数
            while(temp!=0){
                temp=temp/2;
                count++;
            }
            int maxbit=51;
            int one[maxbit+1]={0};
            int zero[maxbit+1]={0};
            for(int i=1;i<=count;i++){//统计每一位zero的个数和one的个数,这里用count,因为只可能有这么多位
                for(int j=0;j<lenA;j++){
                    if((A[j]>>(i-1))%2==0){
                        zero[i]++;
                    }
                    else{
                        one[i]++;
                    }
                }
            }
            for(int i=count+1;i<maxbit;i++){
                zero[i]=lenA;//高位每一位0的个数都是A的长度
            }
            //计算f(i),到第i位之前的最小和
            //没有想最高位是否可行
            long long f[maxbit+1]={0};
            for(int i=1;i<=count;i++){
                if(one[i]>=zero[i]){//令k的第i位等于1
                    int tempf=pow(2,i-1)*zero[i];
                    f[i]=f[i-1]+tempf;
                }
                else{
                    int tempf=pow(2,i-1)*one[i];
                    f[i]=f[i-1]+tempf;
                }
            }
            for(int i=count+1;i<=maxbit;i++){
                f[i]=f[count];
            }
            //从第51位开始,看k能不能是1
            bool k[maxbit+1]={false};
            long long cursum=0;//已经确定下来的值
            for(int i=maxbit;i>=1;i--){
                cursum+=pow(2,i-1)*zero[i];//先尝试让k的这一位是1
                if(cursum+f[i-1]>maxsum){
                    cursum-=pow(2,i-1)*zero[i];
                    cursum+=pow(2,i-1)*one[i];//这一位的K取了0,所以需要加上k xor 1的结果!!!
                    }
                else {
                    k[i]=1;
                }
            }
            long long res=0;
            for(int i=1;i<=maxbit;i++){
                if(k[i]==1){
                    res+=pow(2,i-1);
                }
            }
            if(f[count]>maxsum) return -1;
            else 
            return res;
        }
};
int main(){
    int num,integers,maxsum;
    cin>>num;//case数量
    sel sfunc;
    for(int i=1;i<=num;i++){
        cin>>integers>>maxsum;
        long long A[integers];
        for(int j=0;j<integers;j++){
            cin>>A[j];
        }
        cout << "Case #" << i << ": " <<sfunc.count(A,integers,maxsum)<< endl;
    }
    return 0;
}

容易忽略的点:

1.A最大是达到10^15,N最大是1000,所以最大的数组元素和可能是10^18,比long long 能够表示的最大值

2^63-1=9,233,372,036,854,775,807小,所以用long long是够的,而且必须要用long long

2.注意不存在k时需要取-1,而不是直接返回0,这里取-1的判断可以通过前面算出的f数组,如果f[count]大于M,说明遍历到最高位能够取到的最小值都大于M,因此不存在符合条件的k,而如果f[count]小于等于M,说明一定存在一个符合条件的k。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值