专栏文章
CF2147E
CF2147E题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mint7y34
- 此快照首次捕获于
- 2025/12/02 07:58 3 个月前
- 此快照最后确认于
- 2025/12/02 07:58 3 个月前
对于 ,求使 增加到 的最小操作数。显然,根据二进制的性质,我们会让前若干个 变成 。假设我们让 位变成 ,不难感受到一个贪心:从 位枚举到 位,若当前位为 ,选择代价 最小的 增加使这一位变成 。可以归纳证明每一步都是不劣的,假设处理第 位时我们选择让 增加,因为 ,在贪心策略里我们可以将 增加到 的初始值。
现在直接模拟,复杂度 。
CPP#include<bits/stdc++.h>
using namespace std;
int t,n,m,a[100005],temp[100005];
long long ans[32];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cin>>t;
while(t--){
cin>>n>>m;
int s=0;
for(int i=1;i<=n;i++)cin>>a[i],s|=a[i];
memset(ans,0x3f,sizeof(ans)),ans[__builtin_popcount(s)]=0;
for(int i=0;i<=30;i++){
if(s>>i&1)continue;
long long num=0;
for(int j=1;j<=n;j++)temp[j]=a[j];
for(int j=i;j>=0;j--){
int now=0,pos=1,maxn=0;
for(int k=1;k<=n;k++)now|=temp[k];
if(now>>j&1)continue;
for(int k=1;k<=n;k++)if((temp[k]&((1<<j)-1))>=maxn)pos=k,maxn=temp[k]&((1<<j)-1);
num+=(1<<j)-maxn,temp[pos]+=(1<<j)-maxn;
}
s|=1<<i,ans[__builtin_popcount(s)]=num;
}
for(int i=30;i>=0;i--)ans[i]=min(ans[i],ans[i+1]);
for(int i=1,b;i<=m;i++)cin>>b,cout<<upper_bound(ans,ans+32,b)-ans-1<<'\n';
}
return 0;
}
还可以更快。先预处理,对每个位 将 按照 排序。这可以 完成,因为按 排序后,我们将第 位为 和为 的两个数组归并。接下来枚举 ,过程中对每个 ,我们需要找到第一个未访问的数。这随着 增加是单调不降的,此部分可以 完成。复杂度 。
CPP#include<bits/stdc++.h>
using namespace std;
int t,n,m,a[100005],b[32][100005],mask;
long long ans[32];
bool vis[100005];
bool cmp(int x,int y){
return (a[x]&mask)>(a[y]&mask);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cin>>t;
while(t--){
int s=0;
memset(ans,0x3f,sizeof(ans)),cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i],s|=a[i];
ans[__builtin_popcount(s)]=0;
for(int i=0;i<=31;i++)for(int j=1;j<=n;j++)b[i][j]=j;
mask=(1ll<<31)-1,sort(b[31]+1,b[31]+n+1,cmp);
for(int i=30;i>=0;i--){
for(int j=1;j<=n;j++)b[i][j]=b[i+1][j];
int mid=1;
while(mid<=n&&a[b[i][mid]]>>(i+1)&1)mid++;
mask=(2ll<<i)-1,inplace_merge(b[i]+1,b[i]+mid,b[i]+n+1,cmp);
}
int p[32];
for(int i=0;i<=31;i++)p[i]=1;
for(int i=0;i<=30;i++){
if(s>>i&1)continue;
for(int j=1;j<=n;j++)vis[j]=0;
long long num=0;
for(int j=i;j>=0;j--){
while(p[j]<=n&&vis[b[j][p[j]]])p[j]++;
if(p[j]<=n&&~a[b[j][p[j]]]>>j&1)vis[b[j][p[j]]]=1,num+=(1<<j)-(a[b[j][p[j]]]&((1<<j)-1));
if(p[j]>n)num+=1<<j;
}
s|=1<<i,ans[__builtin_popcount(s)]=num;
}
for(int i=30;i>=0;i--)ans[i]=min(ans[i],ans[i+1]);
for(int i=1,b;i<=m;i++)cin>>b,cout<<upper_bound(ans,ans+32,b)-ans-1<<'\n';
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...