BZOJ 3672 [Noi2014]购票 (熟练剖分+凸壳维护)
题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3672
题意:给出一棵有根树(1为根),边有长度。每个点u有三个属性(len[u],p[u],q[u]),每次u可以转移到u的某个祖先节点v(v满足dist(u,v)<=len[u]),代价为p[u]*dist(u,v)+q[u]。求每个点都转移到1的代价。
思路:首先设f[u]表示u转移到1的最小代价,那么我们可以得到一个DP方程: f[u]=min(f[v]+p[u]*(s[u]-s[v])+q[u]) (v为u的祖先节点,s[u]表示u到根节点1的距离,s[u]-s[v]<=len[u])。
将上面的式子变形:f[u]=(-s[v]*p[u]+f[v])+(s[u]*p[u]+q[u])。其中s[u]*p[u]+q[u]对于u为定值那么我们只要求-s[v]*p[u]+f[v]的最小值即可。
我们按照深度由小大到的顺序依次计算,那么计算u时,它的所有祖先节点都已经计算过,他们的f[v]和s[v]值都已经知道。那么,我们断然是不能一个一个枚举u的所有祖先的.因此如何维护呢?设k=-s[v],b=f[v],x=p[u],y=kx+b,我们现在就是求y的最小值,k<0,斜率k,y轴截距b。我们发现,只要维护[1,fa[u]]这些点组成的上凸壳即可。
我们设现在已经维护了一个上凸壳,现在加入一个点(-s[t],f[t]),我们发现,由于题目说边的距离严格大于0,那么这个s[t]是严格递增的,也就是斜率越来越趋近于负无穷,因此,我们首先将其加入到已经维护的凸壳的最后即可(当x很大时一定是这个新加入的能够使得答案最小)。此时,前面可能有一些不再是最优值,我们只要依次从后向前判断,不是最优值就删掉,因此用一个vector维护即可(不用splay、set什么的了)。
因此,我们得到算法:
(1)首先,树链剖分,用线段树维护每个链,线段树每个节点用一个vector维护这个区间的点组成的上凸壳
(2)按照深度由小到大依次计算。由于这里有一个距离限制,那么对于u,最后我们需要查询的是一段区间[v,fa[u]],v是满足s[u]-s[v]<=len[u]的深度最小的u的祖先。
(3)计算完节点u后将其插入到线段树中包含该点的所有区间。
const i64 inf=(1LL<<62);
const int mod=1000000007;
const int N=200005; vector<int > g[N];
int n,t;
int fa[N];
i64 len[N],p[N],q[N],dis[N]; int sonNum[N];
int dep[N]; i64 s[N]; void DFS(int u)
{
sonNum[u]=1;
int i;
for(i=0;i<SZ(g[u]);i++)
{
int v=g[u][i];
dep[v]=dep[u]+1;
s[v]=s[u]+dis[v];
DFS(v);
sonNum[u]+=sonNum[v];
}
} int id,belong[N],pos[N],mp[N]; void dfs(int u,int root)
{
id++;
belong[u]=root;
pos[u]=id;
mp[id]=u;
int i,k=n+1;
for(i=0;i<SZ(g[u]);i++) if(sonNum[k]<sonNum[g[u][i]]) k=g[u][i];
if(k==n+1) return;
dfs(k,root);
for(i=0;i<SZ(g[u]);i++)
{
if(g[u][i]!=k) dfs(g[u][i],g[u][i]);
}
} struct node
{
int L,R;
vector<pair<i64,i64> > root;
}; node A[N<<2]; void build(int t,int L,int R)
{
A[t].L=L;
A[t].R=R;
A[t].root.clear();
if(L==R)
{
return;
}
int M=(L+R)>>1;
build(t<<1,L,M);
build(t<<1|1,M+1,R);
} #define pdd pair<i64,i64> i64 f[N]; double cross(pdd a,pdd b)
{
return 1.0*(a.second-b.second)/(b.first-a.first);
} int sgn(double x)
{
if(x>1e-20) return 1;
if(x<-1e-20) return -1;
return 0;
} void add(vector<pair<i64,i64> > &x,i64 s,i64 f)
{
pdd p3=MP(s,f);
while(SZ(x)>=2)
{
pdd p2=x[SZ(x)-1];
pdd p1=x[SZ(x)-2];
double x1=cross(p2,p3);
double x2=cross(p1,p3);
if(sgn(x1-x2)==1) break;
x.pop_back();
}
if(SZ(x)==1&&f<=x[0].second) x.pop_back();
x.pb(p3);
} void add(int t,int pos,i64 s,i64 f)
{
add(A[t].root,s,f);
if(A[t].L==A[t].R) return;
int M=(A[t].L+A[t].R)>>1;
if(pos<=M) add(t<<1,pos,s,f);
else add(t<<1|1,pos,s,f);
} i64 curX; i64 get(vector<pdd> x)
{
int L=0,R=SZ(x)-1;
while(R-L>=4)
{
int M=(R+L)>>1;
double xx=cross(x[M-1],x[M]);
if(sgn(xx-curX)>=0) R=M;
else L=M;
}
i64 ans=inf;
int i;
for(i=L;i<=R;i++)
{
pdd p=x[i];
i64 tmp=curX*p.first+p.second;
if(tmp<ans) ans=tmp;
}
return ans;
} i64 cal(int t,int L,int R,i64 len)
{
if(A[t].L==L&&A[t].R==R)
{
int u1=mp[L];
int u2=mp[R];
if(s[u2]-s[u1]<=len) return get(A[t].root); int M=(A[t].L+A[t].R)>>1;
i64 ans=cal(t<<1|1,M+1,R,len);
u1=mp[A[t<<1].R];
len-=(s[u2]-s[u1]);
if(len<0) return ans;
i64 tmp=cal(t<<1,L,M,len);
if(tmp<ans) ans=tmp;
return ans;
}
else
{
int M=(A[t].L+A[t].R)>>1;
if(R<=M) return cal(t<<1,L,R,len);
if(L>M) return cal(t<<1|1,L,R,len);
i64 ans=cal(t<<1|1,M+1,R,len);
int u1=mp[A[t<<1].R];
int u2=mp[R];
len-=(s[u2]-s[u1]);
if(len<0) return ans; i64 tmp=cal(t<<1,L,M,len);
if(tmp<ans) ans=tmp;
return ans;
}
} i64 cal(int u)
{
curX=p[u];
i64 L=len[u];
i64 ans=inf;
while(L>=0)
{
i64 tmp=cal(1,pos[belong[u]],pos[u],L);
if(tmp<ans) ans=tmp;
if(fa[belong[u]]==0) break;
L-=(s[u]-s[fa[belong[u]]]);
u=fa[belong[u]];
}
return ans;
} int main()
{
scanf("%d%d",&n,&t);
int i;
for(i=2;i<=n;i++)
{
scanf("%d%lld%lld%lld%lld",&fa[i],&dis[i],&p[i],&q[i],&len[i]);
g[fa[i]].pb(i);
}
DFS(1);
dfs(1,1);
build(1,1,n);
add(1,pos[1],0,0);
for(i=2;i<=n;i++)
{
int u=mp[i];
f[u]=cal(u)+p[u]*s[u]+q[u];
add(1,pos[u],-s[u],f[u]);
}
for(i=2;i<=n;i++) printf("%lld\n",f[i]);
}
BZOJ 3672 [Noi2014]购票 (熟练剖分+凸壳维护)的更多相关文章
- BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)
前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...
- bzoj 3672: [Noi2014]购票 树链剖分+维护凸包
3672: [Noi2014]购票 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 480 Solved: 212[Submit][Status][D ...
- BZOJ 3672: [Noi2014]购票( 树链剖分 + 线段树 + 凸包 )
s弄成前缀和(到根), dp(i) = min(dp(j) + (s(i)-s(j))*p(i)+q(i)). 链的情况大家都会做...就是用栈维护个下凸包, 插入时暴力弹栈, 查询时就在凸包上二分/ ...
- ●BZOJ 3672 [Noi2014]购票
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3672 题解: 斜率优化DP,点分治(树上CDQ分治...) 这里有一个没有距离限制的简单版: ...
- BZOJ 3672 [NOI2014]购票 (凸优化+树剖/树分治)
题目大意: 略 题面传送门 怎么看也是一道$duliu$题= = 先推式子,设$dp[x]$表示到达$x$点到达1节点的最小花费 设$y$是$x$的一个祖先,则$dp[x]=min(dp[y]+(di ...
- bzoj 3672: [Noi2014]购票
Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的 ...
- BZOJ 3672: [Noi2014]购票 树上CDQ分治
做这道题真的是涨姿势了,一般的CDQ分治都是在序列上进行的,这次是把CDQ分治放树上跑了~ 考虑一半的 CDQ 分治怎么进行: 递归处理左区间,处理左区间对右区间的影响,然后再递归处理右区间. 所以, ...
- 【BZOJ 3672】 3672: [Noi2014]购票 (CDQ分治+点分治+斜率优化)**
3672: [Noi2014]购票 Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国 ...
- BZOJ.1007.[HNOI2008]水平可见直线(凸壳 单调栈)
题目链接 可以看出我们是要维护一个下凸壳. 先对斜率从小到大排序.斜率最大.最小的直线是一定会保留的,因为这是凸壳最边上的两段. 维护一个单调栈,栈中为当前可见直线(按照斜率排序). 当加入一条直线l ...
随机推荐
- html 输入框验证
JS判断只能是数字和小数点 1.文本框只能输入数字代码(小数点也不能输入)<input onkeyup="this.value=this.value.replace(/\D/g,'') ...
- JSP-03-实现数据传递
会话跟踪:隐藏表单域.URL重写和Cookie 3.1 传参方法 get /post 区别 3.2 request 获取参数 数据类型 变量名 = (数据类型)request.getPar ...
- 【RoR win32】安装RoR
在配置好ruby的win 7 命令行下运行, gem install rails 安装成功之后会收到提示,下面就可以用rails建立项目了. 为了提高“rails new”时“bundle insta ...
- yii2中表单的字段标签名称
1.以登陆页面为例,默认是英文的,在loginForm.php中添加attributeLabels,可以变成中文 具体代码如下: public function attributeLabels(){ ...
- 狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(2)
前文链接:狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(1) 小甲鱼在很多情况下是跟着谭浩强鹦鹉学舌,所以谭浩强书中的很多错误他又重复了一次.这样,加上他自己的错误,错谬之处难以胜数. 由于拙 ...
- dota玩家与英雄契合度的计算器,python语言scrapy爬虫的使用
首发:个人博客,更新&纠错&回复 演示地址在这里,代码在这里. 一个dota玩家与英雄契合度的计算器(查看效果),包括两部分代码: 1.python的scrapy爬虫,总体思路是pag ...
- linux文件所属用户和组
使用chown命令可以修改文件或目录所属的用户: 命令:chown 用户 目录或文件名 例如:chown -R qq /home/qq (把home目录下的qq目录的拥有者改为qq用户) 使用chg ...
- win32 treeview
// 1.create treeview DWORD dwStryle = WS_VISIBLE | WS_CHILD | TVS_HASLINES|TVS_SHOWSELALWAYS/*|TVS_L ...
- 快速搭建Redis缓存数据库
之前一篇随笔——Redis安装及主从配置已经详细的介绍过Redis的安装于配置.本文要讲的是如何在已经安装过Redis的机器上快速的创建出一个新的Redis缓存数据库. 一.环境介绍 1) Linux ...
- java利用zxing编码解码一维码与二维码
最近琢磨了一下二维码.一维码的编码.解码方法,感觉google的zxing用起来还是比较方便. 本人原创,欢迎转载,转载请标注原文地址:http://wallimn.iteye.com/blog/20 ...