天天看點

【GDSOI2017第三輪模拟】Travel Plan 背包

題意省略。

分析:對于每個詢問,其實我們把樹上的dfs序求出來,然後每次的詢問其實就是去掉其中的一段區間。那麼我們可以維護一下這個區間的dp字首和字尾,然後詢問的時候合并一下就好了。

怎麼合并:

考慮到這個背包如果把轉移不到的位置去掉,那麼剩下部分就是單調遞增的(就算不單調遞增也可以讓他強行單調遞增),那麼就可以用雙指針來維護啦。

注意詢問要按照左端點排序,然後字首和滾動,不然兩個加在一起空間會GG。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=+; 
int n,m,ban,tot,cnt,now;
typedef long long ll;
const ll inf=;
ll f[*];
ll g[][*];
struct node
{
    int t;
    ll b;
    int id;
}Q[N];
int answ[N];
int  v[N],c[N];
int head[N],next[N],go[N],dfn[N],sum[N];
int a[N];
int Left[N],Right[N];
inline void add(int x,int y)
{
    go[++tot]=y;
    next[tot]=head[x];
    head[x]=tot;
}
int tim=;
bool cmp(node a,node b)
{
    return Left[a.t]<Left[b.t];
}
inline void dfs(int x,int fa)
{
    dfn[x]=++tim;
    a[tim]=x;
    Left[x]=Right[x]=dfn[x];
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa)
        {
            dfs(v,x);
        }
    }
    Right[x]=tim;
}
inline void cal(int lim)
{
    fo(i,now+,lim)
    {
        int x=a[i];
        int m=sum[i];
        fd(j,m,v[x])
        {
            if (j-v[x]==||f[j-v[x]])
            {
                if (!f[j])f[j]=f[j-v[x]]+c[x];
                else f[j]=min(f[j],f[j-v[x]]+c[x]);
            }
        }
        ll mn=inf;
        fd(j,m,)
        if (f[j])
        {
            if (f[j]>=mn)f[j]=;
            else mn=min(mn,f[j]);
        }
    }
    now=lim;
}
int main()
{
    freopen("plan.in","r",stdin);
    freopen("plan.out","w",stdout);
    scanf("%d",&n);
    fo(i,,n-)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    fo(i,,n)scanf("%d%d",&v[i],&c[i]);
    dfs(,);
    fo(i,,n)sum[i]=sum[i-]+v[a[i]];
    fd(i,n,)
    {
        int x=a[i],m=sum[n]-sum[i-];
        fo(j,,v[x]-)g[i][j]=g[i+][j];
        fo(j,v[x],m)
        {
            g[i][j]=g[i+][j];
            if (j-v[x]==||g[i+][j-v[x]])
            {
                if (!g[i][j])g[i][j]=g[i+][j-v[x]]+c[x];
                else g[i][j]=min(g[i][j],g[i+][j-v[x]]+c[x]);
            }
        }
        ll mn=inf;
        fd(j,m,)
        if (g[i][j])
        {
            if (g[i][j]>=mn)g[i][j]=;
            else mn=min(mn,g[i][j]);
        }
    }
    int q;
    scanf("%d",&q);
    fo(i,,q)scanf("%d%lld",&Q[i].t,&Q[i].b),Q[i].id=i;
    sort(Q+,Q++q,cmp);
    fo(i,,q)
    {
        if (Left[Q[i-].t]-!=Left[Q[i].t]-)cal(Left[Q[i].t]-);
        ll b=Q[i].b;
        int id=Q[i].id;
        int l=Left[Q[i].t]-;
        int r=Right[Q[i].t]+;
        int pre=sum[n]-sum[r-]+;
        int ans=,m=sum[l];
        fo(j,,m)
        {
            if (!f[j])continue;
            if (f[j]<=b)ans=max(ans,j);
            while (pre&&(!g[r][pre]||g[r][pre]+f[j]>b))
            {
                pre--;
                if (g[r][pre]&&g[r][pre]<=b)ans=max(ans,pre);
            }
            if (pre)ans=max(ans,pre+j);
        }
        answ[id]=ans;
    }
    fo(i,,q)printf("%d\n",answ[i]);
}