2026CSP-S模拟17 前缀进位
题目描述
给定一个长度为 nn 的非负整数序列 \(a_1\) ,\(a_2\),……,\(a_n\),以及操作次数上限 K。
一次操作需要选择一个整数 \(r(1<=r<=n)\),并把前 \(r\) 个数各增加 1。
你可以进行 0 到 K 次操作后,求下式的最大可能值:
\(a_1\)&\(a_2\)&……&\(a_n\)
输入格式
第一行两个整数 n,K。
第二行 nn 个整数 \(a_1,a_2,\ldots,a_n\)
输出格式
一个整数,答案最大值。
样例
输入:
3 5
1 4 2
输出:
5
数据范围:
$ 1≤ n ≤ 10^6 , 0≤ a_i < 2^{60}, 0≤k<2^{60}$
思路
转换原始式子
设\(c_i\) 为第 i 位加了多少,那么就有:
$$ k ≥ c_1 ≥ c_2 ≥ …… ≥ c_n$$
可以算出操作次数和为\(c_1\)
那么问题可以转换为:
求一个序列c,使得\(c_i \& a_i\)最大,设该值为\(b_i\)。
确定答案
贪心地考虑,肯定是越高位为1越好。
由 $ 0 ≤ a_i < 2^{60}$ 可得,答案最大不会超过60位bit,所以只需要从高位向低位判断该位能不能为1.
如何判断
设当前枚举到的答案为ans,从 \(a_n\) 向前枚举,当前 \(b_i\) 的最小值为 \(a_i + c_{i+1}\)。
在此基础上,找到最高的 \(b_i\) 为0,但是ans为1的 二进制位 ,将该位由1变为0,其余低位全部取0。将加上的数 -> \(c_i\)。最后判断 \(c_i\) 是否 ≤ k。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+100;
int n,k,a[N],m;
signed main(){
freopen("carry.in" , "r" , stdin);
freopen("carry.out" , "w" , stdout);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=60;i>=0;i--){
int s=(1ll<<i),an=m+s,c=0;
bool flag=0;
for(int j=n;j>=1;j--){
int v=c+a[j];
int f=(~v)&an;
if(!f) continue;
int p=(1ll<<(64-__builtin_clzll(f)))-1;
c+=(p&an)-(p&v);
if(c>k){
flag=1;
break;
}
}
if(!flag){
m=an;
}
}
cout<<m;
return 0;
}
一些碎碎念:
遇到一个式子不要着急对原式考虑,可以变换它的适当样子,试着简化原式。
多思考位运算,它会给你想不到的方法
posted on 2026-09-28 17:06 FAYEEEEEEE 阅读(6) 评论(0) 收藏 举报
浙公网安备 33010602011771号