说在前面
zjqqqaq给了一道题让me去写
然后me就乖乖去写了qwq
日常被细节坑
题目
LOJ#6384传送门
看题可戳传送门
解法
看到这种题目的贡献计算方法,决策单调性应该已经比较显然了
发现题目上说,标记不一定在整点,然后这就很不舒服,简单画个图,可以用反证法证明存在一种最优决策,使得所有标记都在整点上(如果把整点上的标记向左/右移动,要么代价为正,要么当前不为最优解)
然后思考如何dp,不难想到一段一段的计算贡献,记上一个标记点为 lf l f ,当前标记点为 rg r g ,可以快速得出 i i 在内的贡献,分三段计算: j∈[lf+1,rg−1] j ∈ [ l f + 1 , r g − 1 ] , j∈[rg,n] j ∈ [ r g , n ] 和 j∈[1,lf] j ∈ [ 1 , l f ]
然后剩下的就是决策单调dp了,使用 分治决策区间 / dp凸优化 / 单调队列 都可以做,这些方法都在me的这篇题解里提到了,这里就不(懒得)写了
下面是代码
me使用的是dp凸优化
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , K ;
long long a[10005] , sa[10005] , sai[10005] ;
struct Data{
int ed , cnt ;
long long val ;
Data( int e = 0 , int c = 0 , long long v = 0 ):ed(e) , cnt(c) , val(v){} ;
bool operator < ( const Data &A ) const {
return val < A.val || ( val == A.val && cnt < A.cnt ) ;
}
} dp[10005] , que[10005] ;
int fr , ba ;
long long Val( int lf , int rg ){
long long rt = 0 ;
int len = rg - lf + 1 ;
if( len <= 2 ) return 0 ;
rt += ( sai[rg-1] - sai[lf+1] - ( sa[rg-1] - sa[lf+1] ) * ( lf + 1 ) ) * 2 ;
rt += ( sai[rg-2] - sai[lf] - ( sa[rg-2] - sa[lf] ) * ( rg - 1 ) ) * 2 ;
rt += ( sa[rg-1] - sa[lf] - a[lf] * ( len - 2 ) ) * lf ;
rt += ( a[rg] * ( len - 2 ) - ( sa[rg-1] - sa[lf] ) ) * ( N - rg + 1 ) ;
return rt ;
}
long long Val( Data &x , int now ){ return Val( x.ed , now ) + x.val ; }
int better( Data &x , Data &y ){
int lf = x.ed , rg = N , rt = N + 1 ;
while( lf <= rg ){
int mid = ( lf + rg ) >> 1 ;
long long t1 = Val( x , mid ) , t2 = Val( y , mid ) ;
if( t1 < t2 || ( t1 == t2 && x.cnt < y.cnt ) )
rt = mid , rg = mid - 1 ;
else lf = mid + 1 ;
} return rt ;
}
void Push( Data &x ){
while( ba > fr ){
int t1 = better( que[ba] , que[ba-1] ) , t2 = better( x , que[ba] ) ;
if( t1 > t2 || ( t1 == t2 && x.cnt < que[ba].cnt ) ) ba -- ;
else break ;
} que[++ba] = x ;
}
Data dp_check( long long delta ){
fr = 1 , que[ ba = 1 ] = Data( 0 , 0 , 0 ) ;
for( int i = 1 ; i <= N ; i ++ ){
while( ba > fr ){
long long v1 = Val( que[fr] , i ) , v2 = Val( que[fr+1] , i ) ;
if( v1 > v2 || ( v1 == v2 && que[fr].cnt > que[fr+1].cnt ) ) fr ++ ;
else break ;
} dp[i] = Data( i , que[fr].cnt + 1 , Val( que[fr] , i ) + delta ) ;
Push( dp[i] ) ;
}
Data res = Data( N , 1e9 , 1e18 ) ;
for( int i = 1 ; i <= N ; i ++ ){
dp[i].val += Val( dp[i].ed , N + 1 ) ;
if( dp[i] < res ) res = dp[i] ;
} return res ;
}
void solve(){
long long lf = 0 , rg = Val( 0 , N + 1 ) , ans ;
for( int t = a[N] / 2 , i = 1 ; i <= N ; i ++ )
if( a[i] <= t && a[i+1] > t ){
rg -= Val( 0 , i ) + Val( i , N + 1 ) ;
break ;
}
while( lf <= rg ){
long long mid = ( lf + rg ) >> 1 ;
Data res = dp_check( mid ) ;
if( res.cnt == K )
return ( void )printf( "%lld\n" , res.val - mid * K ) ;
if( res.cnt < K ) ans = res.val - mid * K , rg = mid - 1 ;
else lf = mid + 1 ;
} printf( "%lld\n" , ans ) ;
}
int main(){
while( scanf( "%d%d" , &N , &K ) != EOF ){
K = min( N , K ) ;
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%lld" , &a[i] ) ;
sa[i] = sa[i-1] + a[i] ;
sai[i] = sai[i-1] + a[i] * i ;
} K ? solve() : (void)printf( "%lld\n" , Val( 0 , N + 1 ) ) ;
}
}