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

来源:互联网 发布:波什生涯数据 编辑:程序博客网 时间:2024/06/10 18:48

题意省略。

分析:对于每个询问,其实我们把树上的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=2e3+5; int n,m,ban,tot,cnt,now;typedef long long ll;const ll inf=1e18;ll f[1005*50];ll g[1005][1005*50];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=0;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+1,lim)    {        int x=a[i];        int m=sum[i];        fd(j,m,v[x])        {            if (j-v[x]==0||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,1)        if (f[j])        {            if (f[j]>=mn)f[j]=0;            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,1,n-1)    {        int x,y;        scanf("%d%d",&x,&y);        add(x,y);        add(y,x);    }    fo(i,1,n)scanf("%d%d",&v[i],&c[i]);    dfs(1,0);    fo(i,1,n)sum[i]=sum[i-1]+v[a[i]];    fd(i,n,1)    {        int x=a[i],m=sum[n]-sum[i-1];        fo(j,0,v[x]-1)g[i][j]=g[i+1][j];        fo(j,v[x],m)        {            g[i][j]=g[i+1][j];            if (j-v[x]==0||g[i+1][j-v[x]])            {                if (!g[i][j])g[i][j]=g[i+1][j-v[x]]+c[x];                else g[i][j]=min(g[i][j],g[i+1][j-v[x]]+c[x]);            }        }        ll mn=inf;        fd(j,m,1)        if (g[i][j])        {            if (g[i][j]>=mn)g[i][j]=0;            else mn=min(mn,g[i][j]);        }    }    int q;    scanf("%d",&q);    fo(i,1,q)scanf("%d%lld",&Q[i].t,&Q[i].b),Q[i].id=i;    sort(Q+1,Q+1+q,cmp);    fo(i,1,q)    {        if (Left[Q[i-1].t]-1!=Left[Q[i].t]-1)cal(Left[Q[i].t]-1);        ll b=Q[i].b;        int id=Q[i].id;        int l=Left[Q[i].t]-1;        int r=Right[Q[i].t]+1;        int pre=sum[n]-sum[r-1]+1;        int ans=0,m=sum[l];        fo(j,1,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,1,q)printf("%d\n",answ[i]);}
0 0
原创粉丝点击