AtCoder Beginner Contest 281

E - Least Elements

https://atcoder.jp/contests/abc281/tasks/abc281_e

Time Limit: 2 sec / Memory Limit: 1024 MB

Score : 500 points

Problem Statement

You are given an integer sequence A=(A1,,AN) of length N, and integers M and K.
For each i=1,,NM+1, solve the following independent problem.

Find the sum of the first K values in the sorted list of the M integers Ai,Ai+1,,Ai+M1 in ascending order.

Constraints

  • 1KMN2×105
  • 1Ai109
  • All values in the input are integers.

Input

The input is given from Standard Input in the following format:

N M K
A1 A2 AN

Output

Let answerk be the answer to the problem for i=k, and print them in the following format:

answer1 answer2 answerNM+1

Sample Input 1

6 4 3
3 1 4 1 5 9

Sample Output 1

5 6 10
  • For i=1, sorting Ai,Ai+1,Ai+2,Ai+3 in ascending order yields 1,1,3,4, where the sum of the first three values is 5.
  • For i=2, sorting Ai,Ai+1,Ai+2,Ai+3 in ascending order yields 1,1,4,5, where the sum of the first three values is 6.
  • For i=3, sorting Ai,Ai+1,Ai+2,Ai+3 in ascending order yields 1,4,5,9, where the sum of the first three values is 10.

Sample Input 2

10 6 3
12 2 17 11 19 8 4 3 6 20

Sample Output 2

21 14 15 13 13

题解

该题算是一种滑动窗口,如果我们在每次滑动窗口时,都进行排序、累加求和,那么肯定是不符合时间限制的。

首先需要意识到,窗口每次滑动时,两次的结果是有某种关联的,我们应该找滑动时的变化规律,而不是每次重新计算。

维护内容

我们维护两个多重集合 l,r(即元素可重复的集合)和求和结果 sum

  • l 的大小为 K,储存长度为 M 的滑窗中,最小的 K个元素。l 中元素之和 sum 即为我们需要的答案。
  • r 的大小为 MK,储存滑窗中剩余的元素。

初始化

首先需要初始化 l,r,sum 在滑动窗口起始位置的值,这个暴力求解即可。

变化规律

我们的滑动窗口向右移动一位,将会造成一个元素离开和一个元素加入,我们分别记为 outin.

outin 的大小做分类讨论,可以得到以下四种情况:

  • out>lmaxin>lmax
  • out<rminin<rmin
  • out<rminin>lmax
  • out>lmaxin<rmin

情况一:out>lmaxin>lmax

离开和加入的元素均比 l 大。需要在 r 中删除 out,加入 insum 不变。

情况二:out<rminin<rmin

离开和加入的元素均比 r 小。需要在 l 中删除 out,加入 in,更新 sumsum+inout

情况三:out<rminin>lmax

离开的元素比 r 小,加入的元素比 l 大。需要在 l 中删除 out,在 r 中加入 in,然后将 rmin 移动给 l,更新 sumsum+rminout

情况四:out>lmaxin<rmin

离开的元素比 l 大,加入的元素比 r 小。需要在 r 中删除 out,在 l 中加入 in,然后将 lmax 移动给 r,更新 sumsum+inlmax

特殊情况

我们上面的分类是假定了 l,r 非空的,当不满足这个条件时就会出问题。

例如 M=K 时就会造成 r 为空,此时需要特判当 r 为空时,进行情况二的操作。

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;

signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int N, M, K;
    cin >> N >> M >> K;
    vector<int> A(N);
    for (int i = 0; i < N; i++)
        cin >> A[i];
    multiset<int> l, r;
    int sum = 0;
    // 初始化l,r,sum的值
    for (int i = 0; i < M; i++)
        l.insert(A[i]);
    while (l.size() > K)
    {
        int mx = *l.rbegin();
        r.insert(mx);
        l.extract(mx);
    }
    for (auto &elem : l)
        sum += elem;
    cout << sum << ' ';
    // 开始递推接下来的值
    for (int i = 1; i + M - 1 < N; i++)
    {
        int out = A[i - 1], in = A[i + M - 1];
        int lmax = *l.rbegin(), rmin = *r.begin();
        if (r.empty())
        {
            l.extract(out);
            l.insert(in);
            sum += in - out;
        }
        else if (out > lmax && in > lmax)
        {
            r.extract(out);
            r.insert(in);
        }
        else if (out < rmin && in < rmin)
        {
            l.extract(out);
            l.insert(in);
            sum += in - out;
        }
        else if (out < rmin && in > lmax)
        {
            r.insert(in);
            int new_rmin = *r.begin();
            r.extract(new_rmin);
            l.insert(new_rmin);
            l.extract(out);
            sum += new_rmin - out;
        }
        else // if (out > lmax && in < rmin)
        {
            l.insert(in);
            int new_lmax = *l.rbegin();
            l.extract(new_lmax);
            r.insert(new_lmax);
            r.extract(out);
            sum += in - new_lmax;
        }
        cout << sum << ' ';
    }
    cout << endl;
    return 0;
}