LOJ#6384.「是男人就过8题——Pony.ai」SignLocation-决策单调性

说在前面

zjqqqaq给了一道题让me去写
然后me就乖乖去写了qwq
日常被细节坑


题目

LOJ#6384传送门
看题可戳传送门


解法

看到这种题目的贡献计算方法,决策单调性应该已经比较显然了
发现题目上说,标记不一定在整点,然后这就很不舒服,简单画个图,可以用反证法证明存在一种最优决策,使得所有标记都在整点上(如果把整点上的标记向左/右移动,要么代价为正,要么当前不为最优解)

然后思考如何dp,不难想到一段一段的计算贡献,记上一个标记点为 l f ,当前标记点为 r g ,可以快速得出 i [ l f + 1 , r g 1 ] 内的贡献,分三段计算: j [ l f + 1 , r g 1 ] j [ r g , n ] 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 ) ) ;
    }
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80353057