bzoj-3672 购票
2015-08-31 17:26
260 查看
题意:
给出一颗n个结点的有根树,边有长度;
每个点有可以购票前往长度相差不超过li的它的祖先,票的花费为pi*长度+qi;
当然的,可以的选择多次倒车到达;
求每个点到根的最小花费;
n<=200000;
题解:
这题真的好贴心,数据特殊情况都给你让你特判了2333;
首先一条链的情况都会吧,设f[i]为i到根的最小花费,dis为到根的距离;
转移方程为:f[i]=f[j]+p[i]*(dis[i]-dis[j])+q[i];
斜率优化搞搞就好了;
然而到了树上,和在序列上的思想基本一样;
但是多了很多细节处理;
分治自然是要分的,分的就应该是树的重心;
分治之后呢?
分治之后的树应该是是有根的,因为所有点的DP值都是由树上的祖先更新而来;
所以就用根到分治中心的这条链,去更新所有分治中心的其他子树;
但是和一般的序列上的不同,我们不能处理完凸包再二分去找答案;
那样会有可选的值遗漏;
所以将要更新的点放在一个序列中,按能够到的祖先排序;
排序之后就在这个序列里扫就好了,时间复杂度是O(L*logL);
然后带上树分治的复杂度,总复杂度O(nlog^2n);
细节颇多,树分治的姿势都是不熟嘛;
代码:
给出一颗n个结点的有根树,边有长度;
每个点有可以购票前往长度相差不超过li的它的祖先,票的花费为pi*长度+qi;
当然的,可以的选择多次倒车到达;
求每个点到根的最小花费;
n<=200000;
题解:
这题真的好贴心,数据特殊情况都给你让你特判了2333;
首先一条链的情况都会吧,设f[i]为i到根的最小花费,dis为到根的距离;
转移方程为:f[i]=f[j]+p[i]*(dis[i]-dis[j])+q[i];
斜率优化搞搞就好了;
然而到了树上,和在序列上的思想基本一样;
但是多了很多细节处理;
分治自然是要分的,分的就应该是树的重心;
分治之后呢?
分治之后的树应该是是有根的,因为所有点的DP值都是由树上的祖先更新而来;
所以就用根到分治中心的这条链,去更新所有分治中心的其他子树;
但是和一般的序列上的不同,我们不能处理完凸包再二分去找答案;
那样会有可选的值遗漏;
所以将要更新的点放在一个序列中,按能够到的祖先排序;
排序之后就在这个序列里扫就好了,时间复杂度是O(L*logL);
然后带上树分治的复杂度,总复杂度O(nlog^2n);
细节颇多,树分治的姿势都是不熟嘛;
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #define N 210000 using namespace std; typedef long long ll; int next ,to ,head ,tot; ll val ; int fa ,size ,st ,top,qu ,s,t,G,mi; ll p ,q ,l ,dis ,f ; bool ban ; void add(int x,int y,ll v) { to[++tot]=y; val[tot]=v; next[tot]=head[x]; head[x]=tot; } void init(int x) { size[x]=1; for(int i=head[x];i;i=next[i]) { dis[to[i]]=dis[x]+val[i]; init(to[i]); size[x]+=size[to[i]]; } } bool cmp(int a,int b) { return dis[a]-l[a]>dis[b]-l[b]; } void getP(int x) { st[++top]=x; for(int i=head[x];i;i=next[i]) if(!ban[to[i]]) getP(to[i]); } ll find(int x) { if(s>t) return 0x3f3f3f3f3f3f3f3fll; int l=s,r=t,mid; while(l<=r) { mid=l+r>>1; if(mid+1>t||(long double)(f[qu[mid+1]]-f[qu[mid]])/(dis[qu[mid+1]]-dis[qu[mid]])<p[x]) r=mid-1; else l=mid+1; } return f[qu[l]]+p[x]*(dis[x]-dis[qu[l]])+q[x]; } void getG(int x,int bk) { size[x]=1; int i,mas=0,temp; for(i=head[x];i;i=next[i]) { if(ban[to[i]]) continue; getG(to[i],bk); size[x]+=size[to[i]]; mas=max(mas,size[to[i]]); } mas=max(mas,bk-size[x]); if(mas<=mi) mi=mas,G=x; } void slove(int x,int y) { if(size[x]==1) return ; int i,j,k; for(i=head[y];i;i=next[i]) if(!ban[to[i]]) ban[to[i]]=1; mi=0x3f3f3f3f; getG(x,size[x]); slove(x,G); top=0; for(i=head[y];i;i=next[i]) getP(to[i]); sort(st+1,st+top+1,cmp); s=1,t=0; for(i=1,k=y;i<=top;i++) { while(k!=fa[x]&&dis[st[i]]-l[st[i]]<=dis[k]) { while(s<t&&(long double)(f[qu[t-1]]-f[qu[t]])/(dis[qu[t-1]]-dis[qu[t]])<(long double)(f[qu[t]]-f[k])/(dis[qu[t]]-dis[k])) t--; qu[++t]=k; k=fa[k]; } f[st[i]]=min(f[st[i]],find(st[i])); } for(i=head[y],k=size[x];i;i=next[i]) { mi=0x3f3f3f3f; getG(to[i],size[to[i]]); slove(to[i],G); } } int main() { int n,m,i,j,k,x,y; ll v; scanf("%d%d",&n,&m); for(i=2;i<=n;i++) { scanf("%d%lld%lld%lld%lld",fa+i,&v,p+i,q+i,l+i); add(fa[i],i,v); } ban[1]=0; init(1); memset(f,0x3f,sizeof(f)); f[1]=0; mi=0x3f3f3f3f; getG(1,size[1]); slove(1,G); for(i=2;i<=n;i++) printf("%lld\n",f[i]); return 0; }
相关文章推荐
- 读书记录(持续更新...)
- 模拟Post请求
- 【EasyUI】Combobox的联动和onChange/onSelect事件绑定
- Goods:动态加载所有的分类项到left.jsp
- setTimeout的用法及注意点
- iOS 官方文档翻译地址已经迁移到github上了
- Android利用Json来进行网络数据传输
- 黑马训练营java学习笔记:数组
- Unity学习笔记(六)---------------GameObject的Active与InActive
- confirm在连接中点击取消依然退出
- 第四坑
- Android最佳性能实践(三)——高性能编码优化
- UnityVS(Visual Studio Tools For Unity)的安装与使用
- 百度地图 v3.5搜索功能,和地图缩放按钮更改样式
- css3中创建动画的三种方式详解
- Win10 通过升级安装完成后出现了中文字体忽大忽小的问题解决。
- C# fixed语句固定变量详解
- 你应该知道的CSS文字大小单位PX、EM、PT
- ThreadLocal
- MyEclipse报错org.apache.jasper.JasperException: Unable to compile class for JSP: