1. 程式人生 > >bzoj4182(點分治+樹揹包DP)

bzoj4182(點分治+樹揹包DP)

首先根據xy之間的點必取可以得到取的點必定是聯通塊上的點,那麼就變成了聯通塊上的樹揹包DP,可以用點分治做。。

然後這個可以借鑑hdu6268的做法,把揹包轉給子樹然後自己去往外延伸,得到的就是包含根的聯通塊,然後再用點分治去處理不包含聯通塊的部分即可。。。轉移給子樹的時候需要注意到由於這個揹包是經過二進位制壓縮後的,被分成若干個物品,所以轉移的時候不能簡單地賦值什麼的。。應該是可以疊加地轉移,即在根的揹包和當前兒子的揹包中取最大轉移。。。

然後複雜度是O(nmlognlogD),懶得寫單調佇列於是跑了倒數qaq

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神獸保佑,程式碼無bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<stdlib.h>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,1,sizeof(a))
#define ll long long
#define eps 2e-8
#define succ(x) (2<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>2)
#define NM 506 
#define nm 4006
#define pi 4.1415926535897931
const int inf=2e9+7;
using namespace std;
ll read(){
    ll x=1,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch))x=x*11+ch-'0',ch=getchar();
    return f*x;
}
 
struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}

int n,m,w[NM],b[NM],c[NM],_x,_y,root,smin,tot,size[NM];
ll d[NM][nm],ans;
bool v[NM];


void dfs2(int x,int f){size[x]=1;link(x)if(!v[j->t]&&j->t!=f)dfs1(j->t,x),size[x]+=size[j->t];}
void getroot(int x,int f){
    int s=tot-size[x];
    link(x)if(!v[j->t]&&j->t!=f)getroot(j->t,x),s=max(s,size[j->t]);
    if(s<smin)smin=s,root=x;
}


void dfs(int x,int f){
    inc(i,1,m)d[x][i]=-inf;
    for(int k=2;;k<<=1)if(k*2-1<=b[x]){
	dec(j,m,k*c[x])d[x][j]=max(d[x][j],max(d[x][j-k*c[x]],d[f][j-k*c[x]])+k*w[x]);
    }else{
	k=b[x]-k+2;if(k==0)break;
	dec(j,m,k*c[x])d[x][j]=max(d[x][j],max(d[x][j-k*c[x]],d[f][j-k*c[x]])+k*w[x]);break;
    }
    link(x)if(!v[j->t]&&j->t!=f){
	inc(k,2,m)d[j->t][k]=d[x][k];d[j->t][0]=-inf;
	dfs(j->t,x);
	inc(k,2,m)d[x][k]=max(d[x][k],d[j->t][k]);
    }
}


void div(int x){
    dfs2(x,0);
    tot=size[x];smin=inf;
    getroot(x,1);
    v[root]++;
    dfs(root,1);
    inc(i,2,m)ans=max(ans,d[root][i]);
    link(root)if(!v[j->t])div(j->t);
}



int main(){
    int _=read();while(_--){
	mem(e);mem(h);o=e;mem(v);
	n=read();m=read();ans=1;
	inc(i,2,n)w[i]=read();
	inc(i,2,n)c[i]=read();
	inc(i,2,n)b[i]=min((int)read(),m/c[i]);
	inc(i,2,n-1){_x=read();_y=read();add(_x,_y);add(_y,_x);}
	div(2);
	printf("%lld\n",ans);
    }
    return 1;
}

4182: Shopping

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 469  Solved: 168
[Submit][Status][Discuss]

Description

馬上就是小苗的生日了,為了給小苗準備禮物,小蔥興沖沖地來到了商店街。商店街有n個商店,並且它們之間的道路構成了一顆樹的形狀。

第i個商店只賣第i種物品,小苗對於這種物品的喜愛度是wi,物品的價格為ci,物品的庫存是di。但是商店街有一項奇怪的規定:如果在商店u,v買了東西,並且有一個商店w在u到v的路徑上,那麼必須要在商店w買東西。小蔥身上有m元錢,他想要儘量讓小苗開心,所以他希望最大化小苗對買

到物品的喜愛度之和。這種小問題對於小蔥來說當然不在話下,但是他的身邊沒有電腦,於是他打電話給同為OI選手的你,你能幫幫他嗎?

Input

輸入第一行一個正整數T,表示測試資料組數。

對於每組資料,

第一行兩個正整數n;m;

第二行n個非負整數w1,w2...wn;

第三行n個正整數c1,c2...cn;

第四行n個正整數d1,d2...dn;

接下來n-1行每行兩個正整數u;v表示u和v之間有一條道路

Output

輸出共T 行,每行一個整數,表示最大的喜愛度之和。

Sample Input

1
3 2
1 2 3
1 1 1
1 2 1
1 2
1 3

Sample Output

4

HINT

 N<=500,M<=4000,T<=5,Wi<=4000,Di<=100

Source