题解:【MX-S13-T2】照相机

题目传送门

题意分析

首先发现,区间不能互相包含是错误的,因为可以简单通过调整法调整为相交,覆盖次数不变,答案也就不变。

答案形如:

\[\sum_{i=1}^nc_ia_i+\sum_{i=1}^na_i \]

且满足 \(c_1=c_n=0,\vert c_i-c_{i+1}\vert\leq1\),因为一个点只能是一个区间的端点。

\(f_{i,j}\) 表示 \(a_1\sim a_i,c_i=j\) 时的最大 \(\displaystyle\sum_{k=1}^ic_ka_k\)

容易得到:

\[f_{i,j}=\max(f_{i-1,j-1},f_{i-1,j},f_{i-1,j+1})+j\cdot a_i \]

时间复杂度 \(\mathcal O\left(n^2\right)\)

发现其具有凹性,因此可以考虑 Slope Trick,将 DP 式里的 \(\max\) 改写为卷积上确界:

\[\begin{aligned} f_i(x)&=\max_{y\in\R}(f_{i-1}(y)+\tilde g(x-y))+x\cdot a_i\\ \tilde g(x)&=\begin{cases} 0&x\in\set{-1,0,1}\\ -\infty&x\not\in\set{-1,0,1} \end{cases} \end{aligned} \]

加入的斜率段为 \(\set{0,0}\),之后再把所有的斜率段的斜率都加上 \(a_i\) 即可。

答案是 \(f_n(0)\),取最大值,只需要维护 \(k\geq0\) 的部分。

考虑维护斜率,实现上为了便于把所有的斜率增加 \(a_i\),记录偏移量 \(\textit{sum}\) 为当前总增加的 \(a_i\) 之和,加入 \(-\textit{sum}\) 后更新 \(\textit{sum}\leftarrow\textit{sum}+a_i\) 即可。

但是注意到此时卷积上确界会导致 \(f_i(-1)=f_{i-1}(0)-a_i\),这是不合法的,所以当最大斜率 \(>0\) 时要弹出(否则不需要,因为)。

现在考虑如何维护答案 \(f_n(0)\)。仍然注意到 \(f_n(-1)=f_{n-1}(0)-a_i\),弹出的斜率为 \(f_n(0)-f_n(-1)=f_n(0)-f_{n-1}(0)+a_i\),因此弹出时更新答案:

\[\textit{ans}\leftarrow\textit{ans}+k \]

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
#define int ll
constexpr const int N=5e5;
constexpr const ll inf=0x3f3f3f3f3f3f3f3f;
int n,a[N+1];
main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n;
	ll ans=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ans+=a[i];
	}
	priority_queue<ll>q;
	ll sum=0;
	for(int i=2;i<=n;i++){
		if(q.size()&&q.top()+sum>0){
			ans+=q.top()+sum;
			q.pop();
			q.push(-sum);
		}
		q.push(-sum);
		sum+=a[i];
	}
	cout<<ans<<'\n';
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-07-01 21:56  TH911  阅读(0)  评论(0)    收藏  举报