Connecting...

P4072 [SDOI2016] 征途

P4072 [SDOI2016] 征途
化简得:

\[m\sum x_i^2-\left( \sum a_i \right)^2 \]

其实只要最小化 \(\sum x_i^2\),其中 \(x_i\)\(i\) 天的和。

\(80 pts\) 暴力:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e3+10,inf=INT_MAX;
int n,m,a[N];
int dp[N][N]/*前i天前j个*/,sum[N];
int sq(int x){
	return x*x;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i],sum[i]=sum[i-1]+a[i];
	for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)dp[i][j]=inf;
	for(int i=1;i<=n;++i)dp[1][i]=sq(sum[i]);
	for(int i=2;i<=m;++i){
		for(int j=i;j<=n;++j){
			for(int k=max(1ll,i-1);k<j;++k){
				dp[i][j]=min(dp[i][j],dp[i-1][k]+sq(sum[j]-sum[k]));
			}
		}
	}
	cout<<dp[m][n]*m-sq(sum[n]);
	return 0;
} 

考虑优化:

\[\begin{equation} \begin{aligned} dp_{i,j}&=\min_{k>=i-1,k>=1,j>=i,j>k} dp_{i-1,k}+\left(sum_j-sum_k\right)^2\\ dp_{i-1,k}+\left(sum_j-sum_k\right)^2&=dp_{i-1,k}+sum_j^2+sum_k^2-2\times sum_j \times sum_k\\ \end{aligned} \end{equation} \]

只和 \(dp_{i,j}\) 有关的是 \(sum_j^2\),无关紧要 。

只需考虑维护 \(dp_{i-1,k}+sum_k^2-2\times sum_j \times sum_k\) 的最小值。

\(x=sum_j\) , \(t=2\times sum_k\) , \(b=dp_{i-1,k}+sum_k^2\)

我们希望维护最优的 \(k\),对于 \(k<k'\),若 \(k'\) 更优,当且仅当 \(b-tx>b'-t'x\),其中 \(t'-t>0\)

等价于 $ x \textgreater \frac{b'-b}{t'-t} = \lambda $

否则前者更优。

我们要营造一个队列,存储可能的最优决策点,其中队首是当前最优决策点。

\(x\) 递增,\(\lambda_1\)head与head+1)一旦小于 \(x\) 队首就废了。如果 \(\lambda\) 递减, \(\lambda_2\) (head+1与head+2)也小于 \(x\) 也废了,全废了,只剩下队尾,所以只能维护递增的 \(\lambda\)

注意:\(k\in[i-1,j-1]\)

所以更新前再入队,卡了我半天。(应该是这个问题吧?)

注意:

检查自己单调队列板子有没有写错,比如我犯了点甜甜的错:while -> if ; 多加了个 else

结束

Code:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define ll long long
const int N=3e3+10,inf=1e18;
ll n,m,a[N];
int dp[N][N]/*前i天前j个*/,sum[N];
int sq(int x){
	return x*x;
}
int head,tail,q[N];
double slope(int i,int x,int y){
	return (double)(dp[i][x]+sq(sum[x])-dp[i][y]-sq(sum[y]))/(double)(2*sum[x]-2*sum[y]);
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i],sum[i]=sum[i-1]+a[i];
	for(int i=0;i<=m;++i)for(int j=0;j<=n;++j)dp[i][j]=inf;
	for(int j=1;j<=n;++j) dp[1][j]=sq(sum[j]);
	for(int i=2;i<=m;++i){
		head=1,tail=0;
		q[++tail]=i-1;
		for(int j=i;j<=n;++j){
			while(head+1<=tail&&slope(i-1,q[head],q[head+1])<sum[j])head++;
			int k=q[head]; 
			dp[i][j]=min(dp[i][j],dp[i-1][k]+sq(sum[j]-sum[k]));
			while(head+1<=tail&&slope(i-1,q[tail-1],q[tail])>slope(i-1,q[tail],j))tail--;
			q[++tail]=j;
		}
	}
	cout<<(ll)(dp[m][n]*m-sq(sum[n]));
	return 0;
}
posted @ 2025-07-26 18:13  余亦宸  阅读(40)  评论(0)    收藏  举报