题目链接:点击查看
题目大意:给出 n 个数组成的数组 a ,再给出 q 次询问,每次询问给出一个 m ,要求找到一个 k ,使得 ( k ^ a[ i ] ) 之和小于等于 m 且 k 最大,若不存在输出 - 1
题目分析:首先我们可以按位储存数组 a ,这个第 k 位对于异或的贡献就是 2^k 了,接下来我们可以通过构造一个 k ,使得 ( k ^ a[ i ] ) 之和最小,这样在每次询问时,如果给出的 m 比最小的 ( k ^ a[ i ] ) 之和还要小,那么答案显然就是 - 1 了,否则的话按位从最高位开始贪心就好了,贪心的策略就是当前的位如果放置 1 且加上其余位置异或的贡献依然小于等于 m ,则肯定放 1 ,否则置 0 即可,由于运算过程中可能会爆 long long ,所以可以暂时地将其转换为 __int128 进行运算
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=60;
__int128 sum[N+1],cnt[N+1][2];
LL solve(__int128 m)
{
if(sum[N]>m)
return -1;
LL ans=0;
for(int i=N;i>=0;i--)
{
if((cnt[i][0]<<i)+(i?sum[i-1]:0)<=m)
{
ans|=(1LL<<i);
m-=(cnt[i][0]<<i);
}
else
m-=(cnt[i][1]<<i);
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
LL num;
scanf("%lld",&num);
for(int j=0;j<=N;j++)
cnt[j][(num>>j)&1]++;
}
for(int i=0;i<=N;i++)
{
sum[i]=min(cnt[i][0],cnt[i][1])<<i;
if(i)
sum[i]+=sum[i-1];
}
int w;
cin>>w;
while(w--)
{
LL m;
scanf("%lld",&m);
printf("%lld\n",solve(m));
}
return 0;
}