您的位置:首页 > 其它

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

2017-04-21 21:19 393 查看
题意省略。

分析:对于每个询问,其实我们把树上的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
;
int answ
;
int  v
,c
;
int head
,next
,go
,dfn
,sum
;
int a
;
int Left
,Right
;
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
-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
-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]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: