题解:【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;
}

浙公网安备 33010602011771号