原题题面
题目描述
给定长度为 的数组 和 次操作,每次操作可以选择相邻的长度为 的数组元素,使其加 。
询问在进行 次操作后,数组 中最小的元素的最大值是多少。
输入格式
第一行包含三个数,为 。
第二行包含 个数,为 。
输出格式
输出一个数,表示最终答案。
input1
6 2 3
2 2 2 2 1 1
output1
2
input2
2 5 1
5 8
output2
9
题目分析
一开始我想的是像 那样暴力枚举所有大小的 。如以下 check 代码:
bool check(int x)
{
vector<int> f;
for (int i = 1; i <= n; i ++) hf[i] = h[i];
for (int i = 1; i <= n; i ++) if (hf[i] < x) f.push_back(i);
int res = 0;
for (int i = 0; i < f.size(); i ++)
{
if (hf[f[i]] >= x) continue;
int t = x - hf[f[i]];
res += t;
int j = i + 1;
while (j < f.size() && f[j] - f[i] < w)
{
hf[f[j]] += t;
j ++;
}
if (res > m) return false;
}
return true;
}
在第一次提交后,发现得到 TLE 的结果,经过仔细计算后,发现可以构造数据将 check 函数卡成 的复杂度,则最终的复杂度为 。
后来经过仔细思考,最终形成以下思路:
首先数组中所有无法达到 check 传值的元素排成一排,则可以按从左到右的顺序进行依次变大,则对于每个元素,我们需要知道是否在其前 w-1 个元素中,存在元素变大进而影响到当前元素。
为此我们可以采用一种 差分思想 的做法。
即定义一个变量统计对当前元素的所有操作数,同时新开一个数组记录对每一个初始操作所能覆盖的最大范围(即 w)。
对某一元素进行判断时只需要比较当前元素及 check 值和操作数之差即可。
复杂度优化为 。
而另一种做法树状数组则在下一文章展开。
Accept代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
int n, m, w;
int h[N], f[N];
bool check(int x)
{
for (int i = 1; i <= n; i ++) f[i] = 0;
int s = 0, res = 0;
for (int i = 1; i <= n; i ++)
{
s += f[i];
if (h[i] >= x - s) continue;
int dif = x - s - h[i];
s += dif, res += dif;
if (i + w <= n) f[i + w] -= dif;
}
return res <= m;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> w;
for (int i = 1; i <= n; i ++) cin >> h[i];
int l = 1, r = 1e10;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << r;
return 0;
}