CF1661D Progressions Covering
题意
有一个 个数的整数序列 ,每次可以选择一个长度为 的连续子区间从左到右分别减去 ,求最少操作多少次可以使所有的 。
分析
我们先抛开如何求解,先想想怎样快速执行一次操作。
执行那么诡异的操作的数据结构肯定是没有的,不过如果我们求出 数组的差分数组的相反数数组 ,那么一次操作 就相当于把 加 ,再把 减去 ,且 ,转化成了我们常见的形式,我这里使用了一种直观而通用的数据结构——树状数组分块。
接下来尝试求解,第一种容易想到的思路是我们从 分别考虑每个 ,在之前做过的操作的基础上执行 次操作 使 ,并对后面的数产生影响,边界情况要特殊处理。
不过这个做法过不了最后一个样例,显然到 时每次操作只减少 是一个极大的浪费,为了尽可能节省次数,我们可以倒序执行操作,在前面的基础上,对于每个 ,执行 次 ,边界情况仍要特殊处理。
倒序处理还有一个好处,就是当处理 时, 已经处理完了,因此将 减去 这个过程可有可无,我们可以偷懒省略掉。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=3e5+10;
int n,k;
ll b[N],a[N],ad[N],sum[N],len,ans;
void add(int l,int r,ll v){//分块的区间修改
int kl=l/len,kr=r/len;
if(kl==kr){
for(int i=l;i<=r;i++){
a[i]+=v;
sum[kl]+=v;
}
return;
}
for(int i=l;i<=kl*len+len-1;i++){
a[i]+=v;
sum[kl]+=v;
}
for(int i=kl+1;i<kr;i++){
ad[i]+=v;
sum[i]+=len*v;
}
for(int i=kr*len;i<=r;i++){
a[i]+=v;
sum[kr]+=v;
}
}
ll ask(int l,int r){//分块的区间求和
ll ans=0;
int kl=l/len,kr=r/len;
if(kl==kr){
for(int i=l;i<=r;i++)
ans+=a[i]+ad[kl];
return ans;
}
for(int i=l;i<=kl*len+len-1;i++)
ans+=a[i]+ad[kl];
for(int i=kl+1;i<kr;i++)
ans+=sum[i];
for(int i=kr*len;i<=r;i++)
ans+=a[i]+ad[kr];
return ans;
}
int main(){
n=read();k=read();
len=sqrt(n);
for(int i=1;i<=n;i++){
b[i]=read();
add(i,i,0-b[i]+b[i-1]);
}
for(int i=n;i>=1;i--){
ll v=ask(1,i);//v就是b[i]
if(v>=0)//已经满足要求就不用计算
continue;
v=-v;//把负数改为正数便于计算
if(i>=k){
add(i-k+1,i,(v/k)+(v%k>0));//(v/k)+(v%k>0)相当于v/k向上取整
ans+=(v/k)+(v%k>0);
}
else{//边界情况
add(1,k,(v/i)+(v%i>0));
ans+=(v/i)+(v%i>0);
}
}
write(ans);
return 0;
}
本文作者:luckydrawbox
本文链接:https://www.cnblogs.com/luckydrawbox/p/18526530
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步