Codeforces Round #262 (Div. 2)
date:2021/11/08
题目大意:
小海狸是一个初级程序员,所以信息学是他最喜欢的科目。很快他的信息学老师就要过生日了,海狸决定为她准备一份礼物。他在窗台上种了一排花,共n朵。开始等待它们长大。然而,过了一段时间,海狸发现花停止生长了。海狸认为赠送小花是不礼貌的。所以他决定想出一些解决办法。 离生日还有m天。第i朵花的高度(假设这一行花从左到右从1到n编号)此时等于ai。在剩下的几天里,海狸每天都要给相邻的w朵花浇水(一天只能浇一次)。在那一天,每朵浇水的花都生长一个高度单位。最后,海狸希望最小的花的高度尽可能大。他能得到的最小的花的最大高度是多少?
题意:
m天内,每天可以选择相邻w个花高度加1,m天后输出花中最小值的最大值;
思路:
考虑二分
答案,check方案是: cnt = 0记录天数,每次遍历一次数组,遇到 a[i] < mid , 让 a[i] 到 a[i+w-1] 均 +1 ,直到a[i] == mid ,cnt 加 变化天数, a[i] >= mid 跳过;
最后比较 cnt 与 m;
bool check(ll mid){
vector<int> b(n+1);
forr(i,1,n) b[i] = a[i];
ll cnt = 0;
forr(i,1,n){
if(b[i] >= mid) continue;
if(mid - b[i] > m) return false;
while(b[i] < mid){
cnt++;
for(int j = i; j <= i+w-1 && j <= n;j++)
b[j]++;
}
}
if(cnt > m) return false;
return true;
}
上述check 是对思路的暴力,T8;
所以考虑优化:上面T的原因在于 check 在 n 与 w 到达最大数据量是 会是 O ( n 2 ) O(n^2) O(n2)级别显然会T;
如何优化呢? 考虑到时间复杂度主要是来自在对w范围的增加操作,如何操作变为 O ( 1 ) O(1) O(1),
开一个 b 数组来记录每个数的变化量,数的变化量主要来自前w的那个数的++操作,这样就可以不用
每次都要循环一次,详细看code;
code
/*
author:@bzdhxs
date:2021//
URL:
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<queue>
using namespace std;
#define _orz ios::sync_with_stdio(false),cin.tie(0)
#define mem(str,num) memset(str,num,sizeof(str))
#define forr(i,a,b) for(int i = a; i <= b;i++)
typedef long long ll;
const int N = 1e5+10;
int n,m,w;
int a[N];
bool check(int mid){
int cnt = 0;
vector<int> b(n+1,0);
int t = 0;// 由w产生的变化量,记录的一场循环的变化;
forr(i,1,n){
t -= i - w >= 1?b[i-w]:0;
if(a[i]+t < mid){
b[i] = mid - a[i] - t;// 即使天数;
cnt += b[i]; // 后面和 m 比较;
t += b[i]; // 变化量;
}
if(cnt > m) return false;
}
return true;
}
int main()
{
scanf("%d %d %d",&n,&m,&w);
forr(i,1,n) cin >> a[i];
int l = 0, r = 1e9+100000;
while(l < r){
int mid = (l+r+1) >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
printf("%d\n",r);
return 0;
}
这里对
t -= i - w >= 1?b[i-w]:0;
解释: 就是说t是一次循环的全局改变量;
比如说 w = 3 , i = 1 ++ 会使 [1, 3] 每个数都++ ,t是记录全局该变量的 当 i = 4 时 已经不属于[1,3]内
故需要 t 减去 [1,3] 内 增加的值;