给定 n 个物品,做多重背包。
给定 q 个询问,每个询问去掉一个物品,求对剩余物品做多重背包的答案。
Data Constraint
n≤1000,q≤3×105
考虑分治,对于当前区间 [L,R] ,只做编号不在这个区间的物品。
然后分治DP就好了。注意要用到单调队列来优化DP。
对于原DP fj=max{fj−kv+kw}
显然可以按照模 v 的余数分组。不妨设当前 j=av+b ,假设我上一个决策点为 kv+b ,那么
时间复杂度: O(nqlogn)
#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 ;
}
以上.