221016周末作业 第k窗口值
题目描述
Tom 最近学习了滑动窗口类的算法,滑动窗口算法可以解决一些线性数组的离线静态区间查询类问题。
具体来说,假设对于一个数组进行 \(m\)次静态区间查询问题。如果这些查询满足条件:\(∀i,j\),当 \(l_i≤l_j时,总有 r_i≤r_j\)(\(i,j\)表示查询的编号,\(l,r\)表示查询的左右端点)
接下来只要查询的问题满足可以快速插入和删除单点,就可以使用滑动窗口优化,将这 \(m\) 次查询的复杂度降低到 \(O(n)\)。
显然,如果对于一个数组的区间查询问题,查询的区间长度给定为 \(k\) 时,总是满足 \(∀i,j\) 当$ l_i≤l_j 时,总有 r_i≤r_j$ 这一条件的。
Tom 接下来想要问你的问题也和定长滑动窗口有关。
众所周知,长度为 \(k\) 的滑动窗口从左到右去截取一个长度大小为 \(n\) 的数组时,一共可以截取到 \(n-k+1\) 个子数组。
Tom 将这 \(n-k+1\) 个子数组的极大值与极小值的乘积求和称为该数组的 "第k窗口值" 。
举个例子,假设长度为 5 的数组为 \([1,5,2,4,3]\)长度为 3 的滑动窗口可以截取三个子数组,它们分别为\([1,5,2],[5,2,4],[2,4,3]\)
所以该数组的“第3窗口值”为 \(1\times 5+2\times 5+2\times 4=23\)
对于一个给定大小的数组 \(n\),Tom 现在想要知道它的第 \(1,2,3,4,5,...,n\) 窗口值各是多少,请你编写程序解决他的问题。
输入格式
第一行输入一个正整数 \(n\),表示数组的大小。
接下来一行输入 \(n\) 个正整数 \(a_i\),表示数组的内容。
输出格式
输出一行 \(n\) 个正整数,表示滑动窗口的长度分别为 \(1,2,3,4,5,...,n\) 时,问题的答案。
输出的整数之间用空格隔开,行末不允许有多余空格。
解析
我们开两个单调栈,分别维护最大值和最小值,然后从左到右遍历过去,这样的话就可以知道当r单调向右滑动的时候,l端点落在不同的位置时最大值最小值各是多少。
由于值域不超过100,那么栈内的元素数目肯定是不会多于100的。所以我们可以暴力穷举栈内元素。然后因为两个单调栈都是有序的,在这里再次借助双指针划窗。提取出 \(max∗min\) 相同的区间长度是 LL 到 RR。使用差分维护一个数组 a,a[k]表示区间长度为k的答案。 然后将使用差分将 a[L]~a[R] 这段区间全部加max∗min即可。最后对差分数组求前缀和,就得到了每一个区间长度的答案。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define ans nowmax * nowmin
using namespace std;
const int N = 1e5 + 7;
int n, a[N], k, d[N], top1, top2;
pair<int, int> stk_max[N], stk_min[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) {
while (top1 && stk_max[top1].fi <= a[i]) -- top1;
while (top2 && stk_min[top2].fi >= a[i]) -- top2;
stk_max[++ top1] = mp(a[i], i);
stk_min[++ top2] = mp(a[i], i);
int nowmax = a[i], nowmin = a[i];
int p1 = top1 - 1, p2 = top2 - 1;
int nowl = i;
while (p1 || p2) {
if (p1 && p2) {
if (stk_max[p1].se >= stk_min[p2].se) {
d[i - nowl + 1] += ans;
d[i - stk_max[p1].se + 1] -= ans;
nowl = stk_max[p1].se;
nowmax = stk_max[p1].fi;
-- p1;
}
else {
d[i - nowl + 1] += ans;
d[i - stk_min[p2].se + 1] -= ans;
nowl = stk_min[p2].se;
nowmin = stk_min[p2].fi;
-- p2;
}
}
else if (p1) {
d[i - nowl + 1] += ans;
d[i - stk_max[p1].se + 1] -= ans;
nowl = stk_max[p1].se;
nowmax = stk_max[p1].fi;
-- p1;
}
else {
d[i - nowl + 1] += ans;
d[i - stk_min[p2].se + 1] -= ans;
nowl = stk_min[p2].se;
nowmin = stk_min[p2].fi;
-- p2;
}
}
d[i - nowl + 1] += ans;
d[i + 1] -= ans;
}
for (int i = 1; i <= n; i ++) {
d[i] += d[i - 1];
printf("%d%c", d[i], i == n ? '\n' : ' ');
}
return 0;
}