牛客 - Final Exam(贪心)

题目链接:点击查看

题目大意:给出 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/106799260
今日推荐