题目链接:Backpack on Tree
每层,对价值除以花费的斜率从大到小排序,然后贪心到恰好大于询问的容量t,接下来开始dp。
设dp[i][j]表示贪心到第i个物品,取出里面花费为j的最小价值。dp2[i][j]表示斜率从小到大(即从右到左)取出花费为j的最大代价。
假设现在贪心得到的价值为t2(因为最大花费为5,所以t2-t<5),然后我枚举从贪心的解里面丢掉花费为x的最小价值dp[i][x],然后从没有加入贪心的物品里取出最大价值的dp2[i+1][x+t-t2],然后更新下最优值即可。因为lcm(1,2,3,4,5)=60,所以枚举的阈值设为70应该就没问题了。
#include <bits/stdc++.h>
using namespace std ;
typedef long long LL ;
typedef pair < int , int > pii ;
#define clr( a , x ) memset ( a , x , sizeof a )
const int MAXN = 20005 ;
const int MAXQ = 200005 ;
const int LIM = 70 ;
struct Node {
int c , v ;
bool operator < ( const Node& a ) const {
return 1LL * v * a.c > 1LL * a.v * c ;
}
} ;
Node a[MAXN] , b[MAXN] ;
vector < int > G[MAXN] ;
vector < pii > Q[MAXN] ;
int cost[MAXN] , val[MAXN] ;
LL dp[MAXN][LIM] , dp2[MAXN][LIM] ;
LL ans[MAXQ] ;
int pre[MAXN] ;
int sumc[MAXN] ;
LL sumv[MAXN] ;
int n , q ;
int cnt ;
void pre_dfs ( int u , int f ) {
for ( int i = 0 ; i < G[u].size () ; ++ i ) {
int v = G[u][i] ;
if ( v == f ) continue ;
pre[v] = u ;
pre_dfs ( v , u ) ;
}
}
void dfs ( int u , int f ) {
b[++ cnt] = a[u] ;
for ( int i = 0 ; i < G[u].size () ; ++ i ) {
int v = G[u][i] ;
if ( v == pre[u] ) continue ;
dfs ( v , u ) ;
}
}
int F ( int x , int l , int r ) {
while ( l < r ) {
int m = l + r >> 1 ;
if ( sumc[m] >= x ) r = m ;
else l = m + 1 ;
}
return l ;
}
long long inf=1LL<<60;
void solve () {
scanf ( "%d" , &n ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
G[i].clear () ;
Q[i].clear () ;
}
for ( int i = 1 ; i < n ; ++ i ) {
int u , v ;
scanf ( "%d%d" , &u , &v ) ;
G[u].push_back ( v ) ;
G[v].push_back ( u ) ;
}
for ( int i = 1 ; i <= n ; ++ i ) {
scanf ( "%d%d" , &a[i].c , &a[i].v ) ;
}
scanf ( "%d" , &q ) ;
for ( int i = 1 ; i <= q ; ++ i ) {
int s , t ;
scanf ( "%d%d" , &s , &t ) ;
Q[s].push_back ( pii ( t , i ) ) ;
ans[i] = -1 ;
}
pre_dfs ( 1 , 0 ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
cnt = 0 ;
dfs ( i , pre[i] ) ;
sort ( b + 1 , b + cnt + 1 ) ;
for ( int j = 0 ; j <= cnt + 1 ; ++ j ) {
for ( int k = 0 ; k < LIM ; ++ k ) {
dp[j][k] = inf ;
dp2[j][k] = -1 ;
}
}
sumc[0] = sumv[0] = 0 ;
for ( int j = 1 ; j <= cnt ; ++ j ) {
sumc[j] = sumc[j - 1] + b[j].c ;
sumv[j] = sumv[j - 1] + b[j].v ;
}
dp[0][0] = 0 ;
dp2[cnt + 1][0] = 0 ;
for ( int j = 0 ; j < cnt ; ++ j ) {
for ( int k = 0 ; k < LIM ; ++ k ) if ( dp[j][k] < inf ) {
dp[j + 1][k] = min ( dp[j + 1][k] , dp[j][k] ) ;
if ( k + b[j + 1].c < LIM ) dp[j + 1][k + b[j + 1].c] = min ( dp[j + 1][k + b[j + 1].c] , dp[j][k] + b[j + 1].v ) ;
}
}
for ( int j = cnt + 1 ; j >= 2 ; -- j ) {
for ( int k = 0 ; k < LIM ; ++ k ) if ( ~dp2[j][k] ) {
dp2[j - 1][k] = max ( dp2[j - 1][k] , dp2[j][k] ) ;
if ( k + b[j - 1].c < LIM ) dp2[j - 1][k + b[j - 1].c] = max ( dp2[j - 1][k + b[j - 1].c] , dp2[j][k] + b[j - 1].v ) ;
}
}
for ( int j = 0 ; j < Q[i].size () ; ++ j ) {
int idx = Q[i][j].second , t = Q[i][j].first ;
if ( t > sumc[cnt] ) {
ans[idx] = -1 ;
continue ;
}
if ( t < LIM ) {
ans[idx] = dp2[1][t] ;
} else {
int x = F ( t , 1 , cnt ) ;
int del = sumc[x] - t ;
for ( int k = del ; k < LIM ; ++ k ) {
if ( dp[x][k] < inf && ~dp2[x + 1][-del + k] ) {
ans[idx] = max ( ans[idx] , sumv[x] - dp[x][k] + dp2[x + 1][-del + k] ) ;
}
}
}
}
}
for ( int i = 1 ; i <= q ; ++ i ) {
printf ( "%lld\n" , ans[i] ) ;
}
}
int main () {
int T ;
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
printf ( "Case #%d:\n" , i ) ;
solve () ;
}
return 0 ;
}