- 你有 朵花,高度为 ,你可以浇 天的水,每天只浇一次。每次浇花的效果是让一个长度 的区间内的所有花的高度 。问 天后最矮的花的高度最大是多少。
- 。
最小值最大,一看就是二分的题目。
二分 表示 天后所有的花的高度都要 。考虑如何判断(即常说的 check()
函数)。
我们可以用一个类似差分与前缀和的方法,记 表示第 朵花被浇了 次, 表示 ,即 数组的差分数组。
我们从前往后扫描,如果第 朵花的高度仍然 的话,我们就浇区间 。利用差分的基本运算可以 完成这一步。
为什么可以直接从前往后扫描呢?因为我们在第 个数后面浇花覆盖 和直接在 浇的效果一模一样。同时由于我们在处理第 个数时已经保证前面的数都 了,所以我们浇 时没必要再浇前面的花了(已经不影响答案了),直接贪心地浇后面即可。
时间复杂度:,空间复杂度:。
const int N=1e5+100;
typedef long long ll;
ll a[N],b[N],c[N<<1];
ll n,m,w,l,r,mid,ans;
inline bool check(ll mid){
register ll x=0,cnt=0;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
b[i]=max(mid-a[i],0ll);
for(int i=1;i<=n;i++){
x+=c[i];//类似于前缀和
if ((b[i]-=x)>0){
if ((cnt+=b[i])>m) break;
x+=b[i];c[i+w]-=b[i];b[i]=0;
}
}
return cnt<=m;
}
int main(){
n=read();m=read();w=read();
for(int i=1;i<=n;i++)
a[i]=read();
l=0;//二分下界
r=5e9;//二分上界
while (l<=r){
mid=(l+r)>>1;
if (check(mid)){
l=mid+1;//尝试更高
ans=mid;//更新答案
}
else r=mid-1;
}
cout<<ans;
return 0;
}
现在你有一个数 ,初始时它为 ,你可以通过下面两种方式之一把 扩大。
- 把 变为
- 把 变为
记 表示从 变成 的最少步数。给定 个数 和询问数 ,每次询问给定两个数 ,求:
共 组输入。
。
首先,这道题最难的是想到如何求 。
我们发现一个奇数 肯定是由 通过第一中转移来的。如果 是偶数时,它可能由 或 转移来。直觉告诉我们从 到 的步数肯定比从 到 少。于是我们可以贪心地规定当 是偶数时,我们从 转移到 。
我们惊喜地发现:这样是对的!!!于是我们可以 的求 !!!
最后一个问题,如果直接求每次的输出会 TLE
。很简单,前缀和就可以了!!!
#define ll long long
const int N=1e5+100;
int test_number,n,q;
ll pre[N],step[N];
int main(){
test_number=read();
while (test_number--){
n=read();q=read();//输入
memset(pre,0,sizeof(pre));
memset(step,0,sizeof(step));
for(int i=1;i<=n;i++){
register ll d=read();
while (d!=1){
if (d%2) step[i]++;
step[i]++;d/=2;
}
pre[i]=pre[i-1]+step[i];
}
for(int i=1,u,v;i<=q;i++){
u=read();v=read();//输入询问
print(pre[v]-pre[u-1],'\n');//输出函数
}
}
return 0;
}