CF2234F 思路分享(单调栈,dp)
https://codeforces.com/problemset/problem/2234/F
题意概述
给定长度为 \(n\) 的序列 \(h\),下标从 \(1\) 开始,定义 \(f(s)\) 为:
-
\(w_s=0\).
-
若 \(\max(w_i,w_{i\mod n + 1}) \gt h_i\),则 \(w_i = w_{i\mod n + 1}\).
-
\(f(s)\) 为序列 \(w\) 所有元素之和的最大值.
求 \(f(1),f(2),\cdots,f(n)\).
\(3\le n \le 2\cdot 10^5\).
思路
考虑以下两种贪心策略:
-
从 \(s\) 顺时针填 \(w\),\(w_i = \max(w_{(i-2+n)\mod n + 1},h_{(i-2+n)\mod n + 1})\).
-
从 \(s\) 逆时针填 \(w\),\(w_i = \max(h_i,w_{i\mod n + 1})\).
不难发现,单独采用任意一种策略都不合法,原因在于转回 \(s\) 后,\(w_s=0\),而贪心填的数是不减的,且一定大于 \(0\).
但是注意到,将两种策略构造的 \(w\) 每个位置取 \(\min\),最终得到的 \(w\) 是合法的,且因为贪心策略的保证,这样是最优的.
按上述做法暴力可以通过 easy version.
记 \(h_{pos}\) 为 \(h\) 中的最大值,观察 \(w\) 的结构可知,在 \(pos\) 之前,\(w_i\) 是前缀最大值;在 \(pos\) 之后,\(w_i\) 是后缀最大值.
对于每个询问,变化的是左右端点分别距离 \(pos\) 的长度,考虑 \(dp\) 求解如上结构.
定义 \(left[i]\) 为左边第一个大于等于 \(i\) 的位置,\(dpr[i]\) 为在 \(pos\) 右端距离为 \(i\) 处的后缀贡献值之和.
转移方程
\(pos\) 左端的前缀贡献部分是类似的,先将环展开成链,为了覆盖所有情况,将 \(pos\) 偏移到 \(pos+n\).
\(left\) 和 \(right\) 使用单调栈求出即可,这里让 \(h[left[i]]\ge h[i]\) 是为了不让 \(i\) 越过 \(pos\).
对于每个询问,分别求出左右端点距离 \(pos\) 的距离 \(d_l,d_r\),答案为 \(dpl[d_l]+dpr[d_r]\).
时间复杂度 \(\mathcal{O}(n)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 9e18;
void solve(){
int n;
cin >> n;
vector<ll> h(2*n+1);
for (int i=1;i<=n;i++){
cin >> h[i];
h[i+n] = h[i];
}
int pos = -1;
ll mx = -INF;
for (int i=1;i<=n;i++){
if (h[i]>mx){
mx = h[i];
pos = i;
}
}
vector<int> left(2*n+1),right(2*n+1);
stack<int> stk;
for (int i=1;i<=2*n;i++){
while (!stk.empty() && h[i]>h[stk.top()]){
stk.pop();
}
left[i] = stk.empty()?0:stk.top();
stk.push(i);
}
while (!stk.empty()){
stk.pop();
}
for (int i=2*n;i>=1;i--){
while (!stk.empty() && h[i]>h[stk.top()]){
stk.pop();
}
right[i] = stk.empty()?2*n+1:stk.top();
stk.push(i);
}
vector<ll> dpr(n);
for (int i=1;i<n;i++){
dpr[i] = dpr[left[pos+i]-pos]+h[pos+i]*(pos+i-left[pos+i]);
}
vector<ll> dpl(n);
for (int i=1;i<n;i++){
dpl[i] = dpl[pos+n-right[pos+n-i]]+h[pos+n-i]*(right[pos+n-i]-(pos+n-i));
}
for (int i=1;i<=n;i++){
int dl,dr;
if (i<=pos){
dl = pos-i;
dr = i+n-1-pos;
}
else{
dl = pos+n-i;
dr = i+n-1-pos-n;
}
cout << dpl[dl]+dpr[dr] << ' ';
}
cout << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号