洛谷 B3799 [NICA #1] 序列 题解(C++)

201 阅读2分钟

B3799 [NICA #1] 序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

本题目前的题解尚未通过所有测试点……

题目描述

小 A 有一个长度为 nn 的序列 a1,a2,ana_1,a_2,\dots a_n。他希望支持两种操作:

  • 1 k,给序列中的每一个元素加上一个整数 kk
  • 2,查询序列中的最大子序列和。

子序列指的是从原序列中去除某些元素(也可以不去除),但不破坏余下元素的相对位置形成的新的序列。例如,对于序列 {2,3,4,5,6}\{2,3,4,5,6\},那么 {2,3,4},{2,4,6}\{2,3,4\},\{2,4,6\} 都是它的子序列,而 {6,5,4}\{6,5,4\} 不是。子序列可以为空,此时子序列和为 00

输入格式

第一行输入两个正整数 n,mn,m,分别表示序列的长度和操作次数。

第二行输入 nn 个正整数 aia_i,表示序列的元素。

第三行开始,往下 mm 行,每一行分别为 1 k 或者 2 的形式,含义如题意所述。

输出格式

对于每个 22 操作,输出一行一个整数表示答案。

样例 #1

样例输入 #1

5 5
-5 12 -7 2 8
2
1 3
2
1 4
2

样例输出 #1

22
31
45

提示

【样例解释】

  • 第一次操作求序列中的最大子序列和,则为 12+2+8=2212+2+8=22
  • 第二次操作让序列中每一个元素加上了 33。此时序列变为 2,15,4,5,11-2,15,-4,5,11
  • 第三次操作求序列中的最大子序列和,则为 15+5+11=3115+5+11=31
  • 第四次操作让序列中每一个元素加上了 44。此时序列变为 2,19,0,9,152,19,0,9,15
  • 第五次操作求序列中的最大子序列和,则为 2+19+9+15=452+19+9+15=45

数据保证,1n,m5×1051 \leq n,m \leq 5\times 10^55×105ai,k5×105-5\times 10^5 \leq a_i,k \leq 5\times 10^5,操作仅为 1122 操作。

题解

思路

序列中的最大子序列和实际上就是序列中所有正数(非负数)的和,本题实际上要求的就是操作后数组中所有正数(非负数)的和,因此,考虑对数组进行排序,记录第一个负数的位置,并模拟对数组的操作。

代码

#include <bits/stdc++.h>

using namespace std;

const int N=1e6+10;
int n, m, num;
int last = 1;
int opt, k;
long long a[N], sum, cnt;

int main() {
    cin >> n >> m;
    //输入数据
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    
    //使用lambda表达式进行降序排序
    sort(a + 1, a + n + 1, [](int x, int y) { return x > y; });
    
    for(int i = 1; i <= n; i++) {
        //last标记了第一个负数的位置
        last = i;
        if(a[i] < 0) break;
        num++;  //统计正数个数
        sum += a[i];
    }
    
    //全正特判
    if(a[last] >= 0) {
        last++;
    }

    for(int i = 1; i <= m; i++) {
        cin >> opt;
        if(opt == 1) {
            cin >> k;
            //有正数变成负数,下标前移
            while(last - 1 >= 1 && a[last - 1] + cnt + k < 0) {
                num--;
                sum -= a[last - 1] + cnt;
                last--;
            }
            
            //cnt是k的和,用于记录多次操作后所加的数值是多少
            cnt += k;
            sum += num * k;
            
            //有负数变成正数,下标后移
            while(last <= n && a[last] + cnt >= 0) {
                num++;
                sum += a[last] + cnt;
                last++;
            }
        }
        else {
            cout << sum << endl;
        }
    }
    return 0;
}