jzoj 5081. 【GDSOI2017第三轮模拟】Travel Plan 背包+dfs序

来源:互联网 发布:dracut命令安装centos 编辑:程序博客网 时间:2024/05/15 23:52

题意

给你一棵树,1为根节点,每个节点上有一件物品。每件物品都不一样且有权值v和花费c。
要求回答q个询问,每个询问t b表示将t为根的子树去掉后,用b元最多可以得到多少权值。
n<=1000,q<=1000,v<=50,c<=4000000

分析

首先费用和肯定是long long级别的了,显然不能对花费做背包。那我们就可以对权值做背包,也就是用f[i,j]表示前i件物品,得到j这么多的权值的最少花费是多少。
转移显然。
现在考虑如何解决删除子树问题。
子树的话。。。显然可以想到dfs序吧。也就是说每次我们要把dfs序上的一段区间删掉,问最大背包。
这样的话显然可以做一次前缀背包,一次后缀背包,然后每次询问就把两个背包合并即可。
那要怎么合并呢?
考虑到这个背包如果把转移不到的位置去掉,那么剩下部分就是单调递增的(就算不单调递增也可以让他强行单调递增),那么就可以用双指针来维护啦。

一开始开了两个1000*50000的int数组,然后被卡long long就强行没掉20分。。。
如果要开long long的话只要把询问按照删除区间的左端点排序,然后让第一个背包数组滚动就好啦。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=1005;const int inf=0x7fffffff;int n,cnt,last[N],dfn,mx[N],mn[N],a[N],v[N],c[N],s[N],now,an[N];LL f[N*50],g[N][N*50];struct edge{int to,next;}e[N*2];struct query{int t,id;LL b;}q[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void addedge(int u,int v){    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;}void dfs(int x,int fa){    a[++dfn]=x;mn[x]=mx[x]=dfn;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].to==fa) continue;        dfs(e[i].to,x);    }    mx[x]=dfn;}bool cmp(query a,query b){    return mn[a.t]<mn[b.t];}bool cmpid(query a,query b){    return a.id<b.id;}void work(int ned){    for (int i=now+1;i<=ned;i++)    {        int x=a[i],lim=s[i];        for (int j=lim;j>=v[x];j--)        {            if (!(j-v[x])||f[j-v[x]]) f[j]=!f[j]?f[j-v[x]]+c[x]:min(f[j],f[j-v[x]]+c[x]);        }        LL mn=1e15;        for (int j=lim;j>=1;j--)            if (f[j])            {                if (f[j]>=mn) f[j]=0;                else mn=min(mn,f[j]);            }    }    now=ned;}int main(){    //freopen("plan.in","r",stdin);freopen("plan.out","w",stdout);    n=read();    for (int i=1;i<n;i++)    {        int x=read(),y=read();        addedge(x,y);    }    for (int i=1;i<=n;i++) v[i]=read(),c[i]=read();    dfs(1,0);    for (int i=1;i<=n;i++) s[i]=s[i-1]+v[a[i]];    for (int i=n;i>=1;i--)    {        int x=a[i],lim=s[n]-s[i-1];        for (int j=0;j<v[x];j++) g[i][j]=g[i+1][j];        for (int j=v[x];j<=lim;j++)        {            g[i][j]=g[i+1][j];            if (!(j-v[x])||g[i+1][j-v[x]]) g[i][j]=!g[i][j]?g[i+1][j-v[x]]+c[x]:min(g[i][j],g[i+1][j-v[x]]+c[x]);        }        LL mn=1e15;        for (int j=lim;j>=1;j--)            if (g[i][j])            {                if (g[i][j]>=mn) g[i][j]=0;                else mn=min(mn,g[i][j]);            }    }    int T=read();    for (int i=1;i<=T;i++) scanf("%d%lld",&q[i].t,&q[i].b),q[i].id=i;    sort(q+1,q+T+1,cmp);    for (int k=1;k<=T;k++)    {        if (mn[q[k-1].t]-1!=mn[q[k].t]-1) work(mn[q[k].t]-1);        LL b=q[k].b;int id=q[k].id,l=mn[q[k].t]-1,r=mx[q[k].t]+1,p=0,q=s[n]-s[r-1]+1,ans=0,lim=s[l];        for (int p=1;p<=lim;p++)        {            if (!f[p]) continue;            if (f[p]<=b) ans=max(ans,p);            while (q&&(!g[r][q]||g[r][q]+f[p]>b))            {                q--;                if (g[r][q]&&g[r][q]<=b) ans=max(ans,q);            }            if (q) ans=max(ans,p+q);        }        an[id]=ans;    }    for (int i=1;i<=T;i++) printf("%d\n",an[i]);    return 0;}
0 0
原创粉丝点击