[CF2043C] Sums on Segments 题解
我们先想全是
-
证明:我们在区间
内任意取一个子区间 。- 定义【扩展】为将一个区间左边或右边添加一个数。
- 定义【收缩】为将一个区间左边或右边去掉一个数。
我们发现所有
的子区间都能通过 【扩展】或【收缩】得到。而对于【扩展】或【收缩】,操作前后的 一定是连续的。于是,所有子区间的 都是连续的。所谓【连续】,当然是从最小和连续到最大和。
将
对于有特殊值的,类似分治的思想,劈两半算两遍,再管包含特殊值的区间。和一定是左半的后缀加特殊值加右半的前缀。同理,只会取到最小值到最大值的所有数。
排序,去重,输出即可。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define all(v) v.begin(), v.end()
using namespace std;
//#define filename "xxx"
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
namespace Traveller {
const int N = 2e5+2;
int n, a[N];
vector<int> ans;
int sum[N];
void work(int *a, int n) {
int x = 0, y = 0;
int mx = 0, mn = 0; //初值的艺术
int sum = 0;
for(int i = 1; i <= n; ++i) {
sum += a[i];
mx = max(mx, sum - y);
mn = min(mn, sum - x);
x = max(x, sum);
y = min(y, sum);
}
for(int i = mn; i <= mx; ++i) ans.push_back(i);
}
void main() {
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i) sum[i] = sum[i-1] + a[i];
int p = -1;
for(int i = 1; i <= n; ++i)
if(abs(a[i]) != 1) {
p = i;
break;
}
ans.clear();
if(~p) {
int mx = 0, mn = 0;
int x = 0, y = 0;
work(a, p-1);
for(int i = p-1; i >= 0; --i)
x = max(x, sum[p-1] - sum[i]), y = min(y, sum[p-1] - sum[i]);
mx += x, mn += y;
x = y = 0;
work(a+p, n-p);
for(int i = p; i <= n; ++i)
x = max(x, sum[i] - sum[p]), y = min(y, sum[i] - sum[p]);
mx += x, mn += y;
for(int i = mn; i <= mx; ++i) ans.push_back(i + a[p]);
}
else work(a, n);
sort(all(ans));
ans.erase(unique(all(ans)), ans.end());
printf("%d\n", (int)ans.size());
for(int ele : ans) printf("%d ", ele);
puts("");
}
}
signed main() {
#ifdef filename
FileOperations();
#endif
int _;
cin >> _;
while(_--) Traveller::main();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步