【斜率優化】luoguP3628 特別行動隊

題面

因爲懶得調格式問題,直接丟傳送門吧。傳送門

解答

一道斜率優化的題,話說斜率優化裏面的平方都是來噁心人的額。
我們考慮寫出一個樸素的dp式子:dp[i]=max{dp[j]+a(sisj)2+b(sisj)+c}dp[i] = max\{dp[j]+a(s_i-s_j)^2+b(s_i-s_j)+c\}
然後丟掉max符號,拆開括號得到dp[i]=dp[j]+a×si22asisj+asj2+bsibsj+cdp[i] = dp[j]+a\times s_i^2-2as_is_j+as_j^2+bs_i-bs_j+c
若決策jj優於kk,那麼用j-k可以的到以下式子:
dp[j]dp[k]2aisisj+2asisk+asj2ask2bsj+bsk>0dp[j]-dp[k]-2a_is_is_j+2as_is_k+as_j^2-as_k^2-bs_j+bs_k>0
進一步化簡,移項得到dp[j]+asj2bsjdp[k]ask2+bsksjsk>2asi\frac{dp[j]+as_j^2-bs_j-dp[k]-as_k^2+bs_k}{s_j-s_k}>2as_i
然後使用單調隊列O(1)O(1)的維護以下就好了。特別注意一點,由於a<0a<0所以我們維護的是一個上凸殼。所以要在取出隊首的時候斜率的大小比較需要相反。代碼如下:

#include <iostream>
#include <cstring> 
#include <cstdio>
using namespace std;
const int MAXN = 1000100;
int read(){
	int x = 0,f = 1;
	char c = getchar();
	while(c<'0'||c>'9'){if(c=='-')f = -1;c = getchar();}
	while(c>='0'&&c<='9'){x = x*10+(c^48);c = getchar();}
	return x*f;
}
long long dp[MAXN];
long long sum[MAXN];
long long a,b,c;
double slope(int j,int k){
	return double(dp[j]+a*(sum[j])*(sum[j])-b*sum[j]-dp[k]-a*(sum[k])*(sum[k])+b*sum[k])/double(sum[j]-sum[k]);
}
int q[MAXN],head,tail;
int n;
int main(){
	n = read(),a = read(),b = read(),c = read();
	long long v;
	for(int i = 1;i<=n;i++){v = read();sum[i] = sum[i-1]+v;}
	head = tail = 1;
	for(int i = 1;i<=n;i++){
		while(head<tail&&slope(q[head],q[head+1])>=2*a*sum[i])head++;//尤其注意這裏的斜率比較問題
		int j = q[head];
		dp[i] = dp[j]+a*(sum[i]-sum[j])*(sum[i]-sum[j])+b*(sum[i]-sum[j])+c;
		while(head<tail&&slope(i,q[tail])>slope(q[tail],q[tail-1]))tail--;
		q[++tail] = i;
	}
	printf("%lld\n",dp[n]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章