【GDSOI2017模拟】Travel Plan

3 篇文章 0 订阅

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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值