[BZOJ4609]-[Wf2016]Branch Assignment-dp凸优化

说在前面

dp凸优化真的是刷榜神器哈哈哈哈
权值log跑得飞快
这里写图片描述
这是CF上非ghost的第一页 emmmm


题目

codeforces Gym 101242传送门
BZOJ4609传送门
看题可戳传送门


解法

这题有一个比较简单的性质,找出来之后就是一个比较常见的决策单调性了

首先我们对正图和反图都跑一遍最短路,求得 1 B B + 1 的最短路
因为一个点的到 B + 1 B + 1 到该点的最短路都会经过分组大小 1 次,所以可以直接令每个点的权值为这两个距离的和
然后一个组的代价就是 ( 组大小 1 ) ( 组内权值和 )

然后现在的问题就是,如何把这些点分组。如果顺序是确定的,那么显然可以dp解决,然而这道题是无序的,我们尝试找一找最优策略。不难发现,我们把所有权值从大到小排序,然后再分组,这样是最优的

证明如下:
假设最优策略中,最大值所在组为 P P 中最小值为 i ,最大的不在 P 中的点为 j ,设 j 所在集合为 Q

  • 如果 | P | > | Q | ,直接将 P 中随便一个点放入 Q ,显然新的解更优秀
  • 接下来只考虑 | P | | Q |
    • 如果 i j ,这就是我们假设的最优策略
    • 如果 i < j ,那么我们将 i , j 互换, P 中代价将增加 ( j 1 ) ( | P | 1 ) ,而 Q 中代价将减少 ( j 1 ) ( | Q | 1 ) ,显然新解不会更差

证毕

所以我们得到两个结论,从大到小排序分组是最优的,并且组的大小越来越大
通过第二个性质可以直接缩减转移点,得到一个 n 2 log 2 n 的做法,代码极短
当然也可以用单调队列来实现 n 2 log 2 n
反正转化到这一步之后,就和codeforces321E差不多了,直接做就ok


下面是代码

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
using namespace std ;

int N , B , S , R ;
struct Path{ int pre , to , len ; } ;
struct dijk_Data{
    int dis , id ;
    dijk_Data( int _ , int __ ):dis(_) , id(__) {} ;
    bool operator < ( const dijk_Data &A ) const {
        return dis > A.dis ;
    }
} ;
struct Data{
    int k , ed ;
    long long val ;
} dp[5005] , que[5005] ;
struct Graph{
    int head[5005] , tp , dist[5005] ;
    Path p[50005] ;
    void In( int t1 , int t2 , int t3 ){
        p[++tp] = ( Path ){ head[t1] , t2 , t3 } ; head[t1] = tp ;
    }

    bool vis[5005] ;
    priority_queue<dijk_Data> que ;
    void dijk(){
        memset( dist , 0x3f , sizeof( dist ) ) ;
        dist[B+1] = 0 , que.push( dijk_Data( 0 , B + 1 ) ) ;
        while( !que.empty() ){
            int u = que.top().id ; que.pop() ;
            if( vis[u] ) continue ; vis[u] = true ;
            for( int i = head[u] ; i ; i = p[i].pre ){
                int v = p[i].to ;
                if( dist[v] > dist[u] + p[i].len ){
                    dist[v] = dist[u] + p[i].len ;
                    que.push( dijk_Data( dist[v] , v ) ) ;
                }
            }
        }
    }
} G1 , G2 ;

long long sum[5005] ;
void preWork(){
    G1.dijk() , G2.dijk() ;
    for( int i = 1 ; i <= B ; i ++ ) sum[i] = G1.dist[i] + G2.dist[i] ;
    sort( sum + 1 , sum + B + 1 , greater<long long>() ) ;
    for( int i = 1 ; i <= B ; i ++ ) sum[i] += sum[i-1] ;
}

int fr , ba ;
long long Val( Data x , int now ){
    return x.val + ( sum[now] - sum[x.ed] ) * ( now - x.ed - 1 ) ;
}

int better( Data x , Data y ){
    int lf = x.ed , rg = B , rt = B + 1 ;
    while( lf <= rg ){
        int mid = ( lf + rg ) >> 1 ;
        long long v1 = Val( x , mid ) , v2 = Val( y , mid ) ;
        if( v1 < v2 || ( v1 == v2 && x.k < y.k ) ) rg = mid - 1 , rt = mid ;
        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 && que[ba].k >= x.k ) ) ba -- ;
        else break ;
    } que[++ba] = x ;
}

void Pop( int i ){
    while( ba > fr ){
        long long v1 = Val( que[fr] , i ) , v2 = Val( que[fr+1] , i ) ;
        if( v1 > v2 || ( v1 == v2 && que[fr].k > que[fr+1].k ) ) fr ++ ;
        else break ;
    }
}

Data calc( long long mid ){
    fr = 1 , ba = 0 , que[++ba] = ( Data ){ 0 , 0 , 0 } ;
    for( int i = 1 ; i <= B ; i ++ ){
        Pop( i ) ;
        dp[i] = ( Data ){ que[fr].k + 1 , i , Val( que[fr] , i ) + mid } ;
        Push( dp[i] ) ;
    } return dp[B] ;
}

void solve(){
    long long lf = 0 , rg = sum[B] * B , ans ;
    while( lf <= rg ){
        long long mid = ( lf + rg ) >> 1 ;
        Data res = calc( mid ) ;
        if( res.k == S )
            return ( void )printf( "%lld\n" , res.val - mid * S ) ;
        if( res.k > S ) lf = mid + 1 ;
        else ans = mid ,rg = mid - 1 ;
    } printf( "%lld\n" , calc( ans ).val - ans * S ) ;
}

int main(){
    scanf( "%d%d%d%d" , &N , &B , &S , &R ) ;
    for( int i = 1 , u , v , l ; i <= R ; i ++ ){
        scanf( "%d%d%d" , &u , &v , &l ) ;
        G1.In( u , v , l ) , G2.In( v , u , l ) ;
    } preWork() ; solve() ;
}

猜你喜欢

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