Description
Solution
很容易看得出是DP,但是在转移的方面有点意思。
如果我们把这个树按dfs序转化成数列之后可以发现,问题变成了:现在有一段连续的dfs序的节点不能够被选则,问除去这一段的其他节点最大贡献是什么?
可以发现,若是转化成数列,就是很简单的背包,但是要如何解决把所有节点的贡献合并起来呢?可以发现,如果我们弄一个DP的前缀值和后缀值,每次询问就很好解决了。但是还有一个问题:虽然贡献是递增的,但是付出的代价并不一定依照这个顺序递增。要想让这个代价也变成单调的其实很容易,只要把DP值贡献由大到小取个代价的min值,表示获得大于等于当前贡献至少要付出的代价。
Code
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define rep(i,x) for(i=la[x];i;i=ne[i])
const int N=1002,MX=5e4+2;
typedef long long ll;
struct arr{
ll l,r;
}t[N];
struct wen{
ll l,r,cost,bh;
}Q[N];
ll la[N],ne[N*2],da[N*2],v[N],c[N],de[N],ans[N],w[N];
ll f[MX],g[N][MX],h[MX];
ll n,i,j,k,zhi,q,x,y,z,sum,num,mx,mj;
ll INF=4e9+1;
bool cmp(wen x,wen y){return x.l<y.l;}
void ins(int x,int y){
da[++sum]=y,ne[sum]=la[x],la[x]=sum;
da[++sum]=x,ne[sum]=la[y],la[y]=sum;
}
void deal(int x,int deep){
int i,j,k;
de[x]=deep;t[x].l=t[x].r=++num;w[num]=x;
rep(i,x) if(!de[da[i]]) deal(da[i],deep+1),t[x].r=t[da[i]].r;
}
int main(){
freopen("plan.in","r",stdin);
freopen("plan.out","w",stdout);
memset(f,127,sizeof(f));f[0]=0;
memset(h,127,sizeof(h));
memset(g,127,sizeof(g));
scanf("%lld",&n);
fo(i,1,n-1) scanf("%lld%lld",&x,&y),ins(x,y);
fo(i,1,n) scanf("%lld%lld",&v[i],&c[i]),mj+=v[i];
scanf("%lld",&q);
fo(i,1,q) scanf("%lld%lld",&Q[i].l,&Q[i].cost),mx=max(mx,Q[i].cost),Q[i].bh=i;
deal(1,1);
fo(i,0,n+1) g[i][0]=0;
fd(i,n,1){
x=w[i];
fd(j,mj,0){
g[i][j]=(g[i][j]<g[i+1][j])?g[i][j]:g[i+1][j];
if(j+v[x]<=mj&&g[i+1][j]<INF)
g[i][j+v[x]]=(g[i][j+v[x]]<g[i+1][j]+c[x])?g[i][j+v[x]]:g[i+1][j]+c[x];
}
}
fo(i,1,n) fd(j,mj-1,0) g[i][j]=(g[i][j]<g[i][j+1])?g[i][j]:g[i][j+1];
fo(i,1,q){
x=Q[i].l;
Q[i].l=(t[x].l-1>0)?t[x].l-1:0;
Q[i].r=(t[x].r+1<=n)?t[x].r+1:n+1;
}
sort(Q+1,Q+q+1,cmp);
zhi=0;
fo(k,1,q){
while(zhi<Q[k].l){
zhi++;x=w[zhi];
fd(j,mj,0){
if(j+v[x]<=mj&&f[j]<INF)
f[j+v[x]]=(f[j+v[x]]<f[j]+c[x])?f[j+v[x]]:f[j]+c[x];
}
fd(j,mj,0) h[j]=(f[j]<h[j+1])?f[j]:h[j+1];
}
x=Q[k].r;j=0;z=Q[k].cost;y=Q[k].bh;
fd(i,mj,0) if(g[x][i]<=z){
while(j<mj&&h[j+1]+g[x][i]<=z) j++;
ans[y]=(ans[y]>i+j)?ans[y]:i+j;
}
}
fo(i,1,q) printf("%lld\n",ans[i]);
}