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
- jzoj 5081. 【GDSOI2017第三轮模拟】Travel Plan 背包+dfs序
- 【GDSOI2017第三轮模拟】Travel Plan 背包
- 【GDSOI2017第三轮模拟】Travel Plan
- [JZOJ5081]. 【GDSOI2017第三轮模拟】Travel Plan
- 【GDSOI2017第三轮模拟】Travel Plan(DP)
- 【JZOJ5081】【GDSOI2017第三轮模拟】Travel Plan
- 【jzoj5081】【GDSOI2017第三轮模拟】【Travel Plan】【动态规划】
- 【GDSOI2017模拟】Travel Plan
- 【GDSOI2017第三轮模拟】Gift
- [JZOJ5082].【GDSOI2017第三轮模拟】Informatics Training
- [JZOJ5083].【GDSOI2017第三轮模拟】Gift
- 【JZOJ5082】【GDSOI2017第三轮模拟】Informatics Training
- JZOJ 5068. 【GDSOI2017第二轮模拟】树
- 【jzoj5083】【GDSOI2017第三轮模拟】【Gift】【快速傅立叶变换】
- 【GDSOI2017第三轮模拟】Informatics Training(码农,平衡树)
- JZOJ 【GDOI2017第三轮模拟day1】单旋
- 【GDSOI2017第二轮模拟】树
- 【GDSOI2017第二轮模拟】树
- 找一篇论文的源代码的方法总结
- (结构体排序)水果
- iOS社招面试题
- left join、right join、inner join的区别
- 史上最简单的 SpringCloud 教程 | 第十四篇: 服务注册(consul)
- jzoj 5081. 【GDSOI2017第三轮模拟】Travel Plan 背包+dfs序
- 云客Drupal8源码分析之实体类型管理器、实体类型、实体Entity
- 浏览器的标准模式和怪异模式之间的区别
- linux编译找不到apr问题解决办法
- C++第四次作业
- Ubuntu 14.04上 安装 OpenJDK8
- shell脚本无法使用source的原因及解决方法
- 通过 console.log($("#MyForm"));,形象的看一下,jQuery对象,都是什么?
- 记录 laravel session失效问题