1. 程式人生 > 實用技巧 >題解 HDU3507 【Print Article】

題解 HDU3507 【Print Article】

題意簡述

給出 \(N\) 個單詞,每個單詞有個非負權值 \(C_{i}\),現要將它們分成連續的若干段,每段的代價為此段單詞的權值和,還要加一個常數 \(M\),即 \((\sum C_{i})^{2}+M\)。現在想求出一種最優方案,使得總費用之和最小

題解

\(f_{i}\) 表示 \(n=i\) 時的答案,轉移方程為:

\[f_{i}=\min\{f_{j}+S_{i..j}^{2}+M\} \]

顯然過不了,推成可以斜率優化的形式。

我們假設決策 \(j\) 優於決策 \(k\)\(k<j<i\)

(一種錯誤的思路)

\[f_{j}+S_{i..j}^{2}+M<f_{k}+S_{i..k}^{2}+M \]

\[f_{j}+(S_{i}-S_{j})^{2}<f_{k}+(S_{i}-S_{k})^2 \]

\[f_{j}+S_{i}^2-2S_{i}S_{j}+S_{j}^{2}<f_{k}+S_{i}^2-2S_{i}S_{k}+S_{k}^{2} \]

\[f_{j}-2S_{i}S_{j}+S_{j}^{2}<f_{k}-2S_{i}S_{k}+S_{k}^{2} \]

\[f_{j}-2S_{i}S_{j}+2S_{i}S_{k}+S_{j}^{2}-S_{k}^{2}-f_{k}<0 \]

\[f_{j}-2S_{i}(S_{j}+ S_{k})+(S_{j}-S_{k})(S_{j}+S_{k})-f_{k}<0 \]

\[f_{j}-(S_{j}+ S_{k})(2S_{i}+S_{j}-S_{k})-f_{k}<0 \]

\[f_{j}-\frac{S_{j}+ S_{k}}{2S_{i}+S_{j}-S_{k}}-f_{k}<0 \]

最後發現做 \(\text{TM}\) 不出來。

於是重新推:

(另一種錯誤思路)(我簡直自閉)

\[f_{j}+S_{i..j}^{2}+M<f_{k}+S_{i..k}^{2}+M \]

\[f_{j}+(S_{i}-S_{j})^{2}<f_{k}+(S_{i}-S_{k})^2 \]

\[f_{j}+S_{i}^2-2S_{i}S_{j}+S_{j}^{2}<f_{k}+S_{i}^2-2S_{i}S_{k}+S_{k}^{2} \]

\[f_{j}-2S_{i}S_{j}+S_{j}^{2}<f_{k}-2S_{i}S_{k}+S_{k}^{2} \]

\[f_{j}-f_{k}+S_{j}^{2}-S_{k}^{2}<2S_{i}S_{j}-2S_{i}S_{k} \]

\[f_{j}-f_{k}+S_{j}^{2}-S_{k}^{2}<2S_{i}(S_{j}-S_{k}) \]

\[f_{j}-f_{k}+(S_{j}-S_{k})(S_{j}+S_{k})<2S_{i}(S_{j}-S_{k}) \]

\[\frac{f_{j}-f_{k}}{2(S_{j}-S_{k})}+\frac{S_{j}+S_{k}}{2}<S_{i} \]

嗯,還是 \(\text{TM}\) 做不來。

好了,推倒重來。

(這次是對的)

\[f_{i}=\min\{f_{j}+S_{i..j}^{2}+M\} \]

\[f_{j}+S_{i..j}^{2}+M<f_{k}+S_{i..k}^{2}+M \]

\[f_{j}+(S_{i}-S_{j})^{2}<f_{k}+(S_{i}-S_{k})^2 \]

\[f_{j}+S_{i}^2-2S_{i}S_{j}+S_{j}^{2}<f_{k}+S_{i}^2-2S_{i}S_{k}+S_{k}^{2} \]

\[f_{j}-2S_{i}S_{j}+S_{j}^{2}<f_{k}-2S_{i}S_{k}+S_{k}^{2} \]

\[f_{j}-f_{k}+S_{j}^{2}-S_{k}^{2}<2S_{i}S_{j}-2S_{i}S_{k} \]

\[f_{j}-f_{k}+S_{j}^{2}-S_{k}^{2}<2S_{i}(S_{j}-S_{k}) \]

\[\frac{(f_{j}+S_{j}^{2})-(f_{k}+S_{k}^{2})}{2(S_{j}-S_{k})}<S_{i} \]

我們令 \(Y_{i}=f_{i}+S_{i}^{2}\)\(X_{i}=2S_{i}\),則我們有:

\[\frac{Y_{j}-Y_{k}}{X_{j}-X_{k}}<S_{i} \]

挺好的,正好就是斜率優化的形式!

斜率優化!

牛逼啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎哎啊啊哎。

那麼接下來我們設 \(F(j,k)=\frac{Y_{j}-Y_{k}}{X_{j}-X_{k}}\)

其中如果滿足 \(F(j,k)<S_{i}\),則我們稱決策 \(j\) 優於決策 \(k\)

現在我們繼續設 \(k<j<i\),那麼如果滿足 \(F(i,j)<F(j,k)\),決策 \(j\) 就永遠不可能成為最優解。

證明不會,\(\text{PPT}\) 沒看懂。

最後放個 \(\text{PPT}\) 裡寫的 \(\text{Summary}\)

1、用一個單調佇列來維護解集。

2、假設佇列中從頭到尾已經有元素 \(a\)\(b\)\(c\)。那麼當 \(d\) 要入隊的時候,維護佇列的上凸性質,即如果 \(F(d,c)<F(c,b)\),那麼刪除點 \(c\)。直到找到 \(F(d,x)\ge F(x,y)\) 為止,並將 \(d\) 點加入在該位置中。

3、求解的時候,從對頭開始,如果已有元素 \(a\)\(b\)\(c\),當 \(i\) 點要求解時,如果 \(F(b,a)<S_{i}\),說明 \(b\) 點比 \(a\) 點更優,\(a\) 點直接排除,於是 \(a\) 出隊,最後 \(f_{i} = \mathrm{getDp}(Q[\mathrm{head}])\)

#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long LL;

namespace MySpace
{
	template<class _T>
	void read(_T &x)
	{
		x = 0;
		char c = getchar();
		_T f = 1;
		while( c < '0' || c > '9' )
		{
			if( c == '-' )	f = -1;
			if( c == -1 )	exit( 0 );
			c = getchar();
		}
		while( c >= '0' && c <= '9' )	x = x * 10 + c - '0', c = getchar();
		x *= f;
	}
	
	template<class _T>
	void write(_T x)
	{
		if( x < 0 )	putchar( '-' ), x = -x;
		if( x > 9 )	write( x / 10 );
		putchar( x % 10 + '0' );
	}
	
	const LL MAXN = 5e5 + 5;
	LL N, M, Dp[MAXN], Q[MAXN], S[MAXN], A[MAXN];
	
	LL Square( LL x ) { return x * x; }
	LL Up( LL i ) { return Dp[i] + Square( S[i] ); }
	LL Down( LL i ) { return S[i] << 1; }
	LL FracUp( LL j, LL k ) { return Up( j ) - Up( k ); }
	LL FracDown( LL j, LL k ) { return Down( j ) - Down( k ); }
}

int main()
{
	using namespace MySpace;
	while( ~ scanf( "%lld%lld", &N, &M ) )
	{
		for( LL i = 1; i <= N; ++ i )	scanf( "%lld", &A[i] ), S[i] = S[i - 1] + A[i];
		LL l = 1, r = 1;
		Q[r] = 0;
		for( LL i = 1; i <= N; ++ i )
		{
			while( l < r && FracUp( Q[l + 1], Q[l] ) < S[i] * FracDown( Q[l + 1], Q[l] ) )	l ++;
			Dp[i] = Dp[Q[l]] + Square( S[i] - S[Q[l]] ) + M;
			while( l < r && FracUp( i, Q[r] ) * FracDown( Q[r], Q[r - 1] ) <= FracUp( Q[r], Q[r - 1] ) * FracDown( i, Q[r] ))	r --;
			Q[++ r] = i;
		}
		printf( "%lld\n", Dp[N] );
	}
//	while( read( N ), read( M ), 1 )
//	{
//		for( LL i = 1; i <= N; ++ i )	read( A[i] ), S[i] = S[i - 1] + A[i];
//		LL l = 1, r = 0;
//		Q[l] = 0;
//		for( LL i = 1; i <= N; ++ i )
//		{
//			while( l <= r && FracUp( Q[l + 1], Q[l] ) < S[i] * FracDown( Q[l + 1], Q[l] ) )	l ++;
//			Dp[i] = Dp[Q[l]] + Square( S[i] - S[Q[l]] ) + M;
//			while( l <= r && FracUp( i, Q[r - 1] ) * FracDown( Q[r - 1], Q[r - 2] ) < FracUp( Q[r - 1], Q[r - 2] ) * FracDown( i, Q[r - 1] ))	r ++;
//			Q[++ r] = i;
//		}
//		write( Dp[N] ), putchar( '\n' );
//	}
	return 0;
}