B3799 [NICA #1] 序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
本题目前的题解尚未通过所有测试点……
题目描述
小 A 有一个长度为 的序列 。他希望支持两种操作:
1 k,给序列中的每一个元素加上一个整数 ;2,查询序列中的最大子序列和。
子序列指的是从原序列中去除某些元素(也可以不去除),但不破坏余下元素的相对位置形成的新的序列。例如,对于序列 ,那么 都是它的子序列,而 不是。子序列可以为空,此时子序列和为 。
输入格式
第一行输入两个正整数 ,分别表示序列的长度和操作次数。
第二行输入 个正整数 ,表示序列的元素。
第三行开始,往下 行,每一行分别为 1 k 或者 2 的形式,含义如题意所述。
输出格式
对于每个 操作,输出一行一个整数表示答案。
样例 #1
样例输入 #1
5 5
-5 12 -7 2 8
2
1 3
2
1 4
2
样例输出 #1
22
31
45
提示
【样例解释】
- 第一次操作求序列中的最大子序列和,则为 ;
- 第二次操作让序列中每一个元素加上了 。此时序列变为 ;
- 第三次操作求序列中的最大子序列和,则为 ;
- 第四次操作让序列中每一个元素加上了 。此时序列变为 ;
- 第五次操作求序列中的最大子序列和,则为 。
数据保证,,,操作仅为 或 操作。
题解
思路
序列中的最大子序列和实际上就是序列中所有正数(非负数)的和,本题实际上要求的就是操作后数组中所有正数(非负数)的和,因此,考虑对数组进行排序,记录第一个负数的位置,并模拟对数组的操作。
代码
#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;
}