题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1010
方程:f( i ) = min( cost( 1 , i ) , f( j - 1 ) + cost( j , i ) ) ( cost( i , j ) 表示从i个玩具到j个连续放入的花费 )
然后推导出斜率:
K( i , j ) = ( Y( i ) - Y( j ) ) / ( X( i ) - X( j ) )
X( i ) = s[ i - 1 ] + i Y( i ) = f[ i - 1 ] + X( i ) ^ 2
当 j > k 且 K( j , k ) < 2 ( s[ i ] + i - L ) 时 , 状态j优于k,然后单调队列维护斜率。
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std ;
#define MAXN 50010
#define ll long long
int n ;
ll f[ MAXN ] , s[ MAXN ] , l ;
ll X( int j ) {
return s[ j - 1 ] + j ;
}
ll Y( int j ) {
return f[ j - 1 ] + X( j ) * X( j ) ;
}
double K( int i , int j ) {
ll x = X( i ) - X( j ) , y = Y( i ) - Y( j ) ;
double k = double( y ) / double( x ) ;
return k ;
}
ll cost( int i , int j ) {
ll ret = ( s[ j ] - s[ i - 1 ] + j - i - l ) ;
return ret * ret ;
}
int Q[ MAXN ] , head = 1 , tail = 0 ;
int main( ) {
scanf( "%d%lld" , &n , &l ) ;
s[ 0 ] = 0 ;
for ( int i = 0 ; i ++ < n ; ) {
ll c ; scanf( "%lld" , &c ) ;
s[ i ] = s[ i - 1 ] + c ;
}
for ( int i = 0 ; i ++ < n ; ) {
f[ i ] = cost( 1 , i ) ; ll A = s[ i ] + i - l ;
while ( head < tail && K( Q[ head ] , Q[ head + 1 ] ) <= 2 * double( A ) ) head ++ ;
if ( head <= tail ) f[ i ] = min( f[ i ] , f[ Q[ head ] - 1 ] + cost( Q[ head ] , i ) ) ;
while ( head < tail && K( Q[ tail - 1 ] , Q[ tail ] ) >= K( Q[ tail ] , i + 1 ) ) tail -- ;
Q[ ++ tail ] = i + 1 ;
}
printf( "%lld\n" , f[ n ] ) ;
return 0 ;
}