Present(差分/树状数组)

102 阅读2分钟

Problem - C - Codeforces

原题题面

image.png

题目描述

给定长度为 nn 的数组 aamm 次操作,每次操作可以选择相邻的长度为 ww 的数组元素,使其加 11

询问在进行 mm 次操作后,数组 aa 中最小的元素的最大值是多少。

输入格式

第一行包含三个数,为 n,m,w(1wn105;;1m105)n, m, w(1 \le w\le n\le 10^5;; 1\le m \le 10^5)

第二行包含 nn 个数,为 a_1,a_2an(1ai109)a\_1, a\_2 \cdots a_n(1\le a_i \le 10^9)

输出格式

输出一个数,表示最终答案。

input1

6 2 3
2 2 2 2 1 1

output1

2

input2

2 5 1
5 8

output2

9

题目分析

一开始我想的是像 w=1w=1 那样暴力枚举所有大小的 ww。如以下 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 函数卡成 O(n2)O(n^2) 的复杂度,则最终的复杂度为 O(n2logn)O(n^2logn)

后来经过仔细思考,最终形成以下思路:

首先数组中所有无法达到 check 传值的元素排成一排,则可以按从左到右的顺序进行依次变大,则对于每个元素,我们需要知道是否在其前 w-1 个元素中,存在元素变大进而影响到当前元素。

为此我们可以采用一种 差分思想 的做法。

即定义一个变量统计对当前元素的所有操作数,同时新开一个数组记录对每一个初始操作所能覆盖的最大范围(即 w)。

对某一元素进行判断时只需要比较当前元素及 check 值和操作数之差即可。

复杂度优化为 O(nlogn)O(nlogn)

而另一种做法树状数组则在下一文章展开。

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;
}