3223. HEOI2013 Ede的新背包问题

题目大意

给定 n 个物品,做多重背包。
给定 q 个询问,每个询问去掉一个物品,求对剩余物品做多重背包的答案。

Data Constraint
n1000,q3×105

题解

考虑分治,对于当前区间 [L,R] ,只做编号不在这个区间的物品。
然后分治DP就好了。注意要用到单调队列来优化DP。


单调队列优化多重背包

对于原DP fj=max{fjkv+kw}
显然可以按照模 v 的余数分组。不妨设当前 j=av+b ,假设我上一个决策点为 kv+b ,那么

fav+b=max{fav+b(ak)v+(ak)w}

fav+b=max{fb+kvkw}+aw

所以用一个单调队列维护 fb+kvkw 就好了。


时间复杂度: O(nqlogn)

SRC

#include
#include
#include
#include
#include
using namespace std ;

#define N 1000 + 10
const int MAXN = 1000 ;
struct Object {
    int a , b , c ;
} P[N] ;

int Qv[N] , Qx[N] ;
int f[15][N] , ans[N][N] ;
int n , m , Cnt ;

void Calc( int l , int r ) {
    for (int i = 0 ; i <= MAXN ; i ++ ) f[Cnt][i] = f[Cnt-1][i] ;
    for (int i = l ; i <= r ; i ++ ) {
        int v = P[i].a , w = P[i].b , c = P[i].c ;
        for (int b = 0 ; b < v ; b ++ ) {
            int head = 1 , tail = 0 ;
            for (int a = 0 ; a * v + b <= MAXN ; a ++ ) {
                int last = f[Cnt][a*v+b] - a * w ;
                while ( tail >= head && Qv[tail] < last ) tail -- ;
                ++ tail ;
                Qv[tail] = last ;
                Qx[tail] = a ;
                while ( head <= tail && a - Qx[head] > c ) head ++ ;
                f[Cnt][a*v+b] = Qv[head] + a * w ;
            }
        }
    }
}

void DIV( int l , int r ) {
    if ( l == r ) {
        for (int i = 0 ; i <= MAXN ; i ++ ) ans[l][i] = f[Cnt][i] ;
        return ;
    }
    ++ Cnt ;
    int mid = (l + r) / 2 ;
    Calc( mid + 1 , r ) ;
    DIV( l , mid ) ;
    Calc( l , mid ) ;
    DIV( mid + 1 , r ) ;
    Cnt -- ;
}

int main() {
    scanf( "%d" , &n ) ;
    for (int i = 1 ; i <= n ; i ++ ) scanf( "%d%d%d" , &P[i].a , &P[i].b , &P[i].c ) ;
    DIV( 1 , n ) ;
    scanf( "%d" , &m ) ;
    for (int i = 1 ; i <= m ; i ++ ) {
        int d , e ;
        scanf( "%d%d" , &d , &e ) ;
        d ++ ;
        printf( "%d\n" , ans[d][e] ) ;
    }
    return 0 ;
}

以上.

你可能感兴趣的:(3223. HEOI2013 Ede的新背包问题)