abc407 F 题题解
abc407 F 题题解
这是另一个正解的代码,有需要的话,请自行食用。
前言
这个做法理论上可以卡到 \(O(N^2)\),但是我们充分利用 \(AtCoder\) 的脚造数据,愉快的以近似线性的时间复杂度通过本题。
做法
首先,我们肯定不能直接暴力去算,这样是严格 \(O(N^2)\) 的,肯定过不了。
那么我们考虑拆贡献。
考虑单独去算每一个元素的贡献。
考虑一个元素在什么情况下会有贡献。
显然是在这个元素是一个区间内的最大值的时候算贡献。
怎么算一个元素的最大伸展长度?
可以通过正反两个单调栈解决。
如果不会单调栈的话,请自行解决。
说到这里,考虑怎么去算。
肯定这个大区间内部的每一个小区间最大值都是这个值。
但是直接去枚举的话,总时间复杂度 \(O(n^3)\),爆炸。
那么直接枚举左端点,右端点在 \([i,r]\) 范围内肯定都行。
那么这么枚举还是 \(O(N^2)\) 的,显然会死。
所以有一个小小的优化,能直接干爆脚造数据。
就是看左边和右边哪个元素更少。
虽然这个很容易卡爆,但是还是因为数据太烂,他就活了下来,得到了和线性一样的时间复杂度。
然后呢?
我们可以通过差分数组维护这个 \(k\) 为多少的时候的答案,到最后前缀和一下,然后输出就行了。
具体看代码吧。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n;
int a[N];
int c[N];
int lef[N];
int rig[N];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1;i <= n;++i) {
cin >> a[i];
}
stack<int> st;
for (int i = 1;i <= n;++i) {
while (!st.empty() && a[st.top()] <= a[i]) st.pop();
if (st.empty()) lef[i] = 1;
else lef[i] = st.top() + 1;
st.push(i);
}
st = stack<int>();
for (int i = n;i >= 1;--i) {
while (!st.empty() && a[st.top()] < a[i]) st.pop();
if (st.empty()) rig[i] = n;
else rig[i] = st.top() - 1;
st.push(i);
}
for (int i = 1;i <= n;++i) {
int l = lef[i];
int r = rig[i];
//确定左右端点
if (i - l <= r - i) {
//如果左边的点的数量<=右边的,枚举左端点
for (int j = l;j <= i;++j) {
//枚举区间左端点
int len1 = i - j + 1;
int len2 = r - j + 1;
int mxlen = max(len1, len2);
int mnlen = min(len1, len2);
c[mnlen] += a[i];
c[mxlen + 1] -= a[i];
}
}
else {
for (int j = i;j <= r;++j) {
int len1 = j - i + 1;
int len2 = j - l + 1;
int mxlen = max(len1, len2);
int mnlen = min(len1, len2);
c[mnlen] += a[i];
c[mxlen + 1] -= a[i];
}
}
}
for (int i = 1;i <= n;++i) c[i] += c[i - 1];
for (int i = 1;i <= n;++i) {
cout << c[i] << '\n';
}
cout << "\n";
return 0;
}

浙公网安备 33010602011771号