您的位置:首页 > 其它

[JZOJ5081]. 【GDSOI2017第三轮模拟】Travel Plan

2017-04-22 09:50 387 查看

题目描述





分析

做这道题的时候很迷啊,什么都想不到。

首先弄成dfs序,那么每次询问就是有一个区间的不能选。

权值这么小,肯定设f[i][j]表示做到dfs序数组的第i个点,凑出价值为j的最小代价嘛。

现在其实要考虑的是怎么合并两段前缀和后缀数组嘛。

然而我们发现直接合并要n^2*v^2,根本做不了。考虑枚举前缀的价值x1,那么我们肯定要让后缀价值x2尽量大,然后因为代价是单调上升的,我们x1从小到大枚举,x2就会一直往下降。所以指针扫一遍就好了,一个询问时间是n*v的。

注意一些时间上的优化。

另外由于这题开long long 会爆空间,要用unsigned int

值得注意的是,unsigned int用%d提示符读入,会鬼畜,必须小心。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef unsigned int ui;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=1005;
const ui mx=4e9;
ui f
[N*50],g
[N*50],z,c
,tmp,H1,H2;
int siz
,rev
,dfn
,x,y,i,j,n,l,r,df,lim,l1,l2,q,topf
,topg
,v
,kx;
int tt,b[N*2],next[N*2],first
;
void cr(int x,int y)
{
tt++;
b[tt]=y;
next[tt]=first[x];
first[x]=tt;
}
void dfs(int x,int y)
{
df++;
dfn[x]=df;
rev[df]=x;
siz[x]=1;
for(int p=first[x];p;p=next[p])
if (b[p]!=y)
{
dfs(b[p],x);
siz[x]+=siz[b[p]];
}
}
void predo()
{
lim=kx*n;
topf[0]=0;topg[n+1]=0;
fo(l,1,n)
{
i=rev[l];
fo(j,0,v[i]-1) f[l][j]=f[l-1][j];
fo(j,v[i],topf[l-1]) f[l][j]=min(f[l-1][j],f[l-1][j-v[i]]+c[i]);
topf[l]=min(lim,topf[l-1]+v[i]);
fo(j,topf[l-1]+1,topf[l]) f[l][j]=f[l-1][j-v[i]]+c[i];
fd(j,topf[l]-1,0) f[l][j]=min(f[l][j+1],f[l][j]);
}
fd(l,n,1)
{
i=rev[l];
fo(j,0,v[i]-1) g[l][j]=g[l+1][j];
fo(j,v[i],topg[l+1]) g[l][j]=min(g[l+1][j],g[l+1][j-v[i]]+c[i]);
topg[l]=min(lim,topg[l+1]+v[i]);
fo(j,topg[l+1]+1,topg[l]) g[l][j]=g[l+1][j-v[i]]+c[i];
fd(j,topg[l]-1,0) g[l][j]=min(g[l][j+1],g[l][j]);
}
}
int solve(int l,int r)
{
l1=0;
l2=topg[r];
tmp=0;
int ret=0;
fo(l1,0,topf[l])
{
while (l2&&g[r][l2]+f[l][l1]>z) l2--;
if (g[r][l2]+f[l][l1]<=z) ret=max(ret,l1+l2);
}
return ret;
}
int main()
{
freopen("plan.in","r",stdin);
freopen("plan.out","w",stdout);
scanf("%d",&n);
fo(i,1,n-1)
{
scanf("%d %d",&x,&y);
cr(x,y);
cr(y,x);
}
dfs(1,0);
fo(i,1,n)
{
scanf("%u %u",v+i,c+i);
kx=max(kx,v[i]);
}
scanf("%d",&q);
predo();
fo(i,1,q)
{
scanf("%d %u",&x,&z);
l=dfn[x]-1;
r=dfn[x]+siz[x];
printf("%d\n",solve(l,r));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: