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\) 处的后缀贡献值之和.

转移方程

\[dpr[i] = dpr[left[pos+i]-pos]+h[pos+i]\cdot (pos+i-left[pos+i]) \]

\(pos\) 左端的前缀贡献部分是类似的,先将环展开成链,为了覆盖所有情况,将 \(pos\) 偏移到 \(pos+n\).

\[dpl[i]=dpl[pos+n-right[pos+n-i]]+h[pos+n-i]\cdot(right[pos+n-i]-(pos+n-i)) \]

\(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;
}
posted @ 2026-06-13 18:31  kzssCCC  阅读(22)  评论(0)    收藏  举报