革命尚未成功,同志还需努力。

Description

  今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会。
  全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号。其中SZ市的编号为 1。对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv
  从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达 a。再选择城市 a 的一个祖先 b,支付费用并到达 b。以此类推,直至到达SZ市。
  对于任意一个城市 v,我们会给出一个交通工具的距离限制 lv。对于城市 v 的祖先 a,只有当它们之间所有道路的总长度不超过 lv  时,从城市 v 才可以通过一次购票到达城市 a,否则不能通过一次购票到达。对于每个城市 v,我们还会给出两个非负整数 pv,qv  作为票价参数。若城市 v 到城市 a 所有道路的总长度为 d,那么从城市 v 到城市 a 购买的票价为 dpv+qv
  每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。

Input

  第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。
  输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市。其中第 v 行包含 5 个非负整数 f_v,s_v,p_v,q_v,l_v,分别表示城市 v 的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。
  请注意:输入不包含编号为 1 的SZ市,第 2 行到第 n 行分别描述的是城市 2 到城市 n。

Output

  输出包含 n-1 行,每行包含一个整数。其中第 v 行表示从城市 v+1 出发,到达SZ市最少的购票费用。同样请注意:输出不包含编号为 1 的SZ市。

Sample Input

  7 3
  1 2 20 0 3
  1 5 10 100 5
  2 4 10 10 10
  2 9 1 100 10
  3 5 20 100 10
  4 4 20 0 10

Sample Output

  40
  150
  70
  149
  300
  150

HINT

  对于所有测试数据,保证 0≤pv≤106,0≤qv≤1012,1≤fv<v;保证 0<sv≤lv≤2×1011,且任意城市到SZ市的总路程长度不超过 2×1011
  输入的 t 表示数据类型,0≤t<4,其中:
  当 t=0 或 2 时,对输入的所有城市 v,都有 fv=v-1,即所有城市构成一个以SZ市为终点的链;
  当 t=0 或 1 时,对输入的所有城市 v,都有 lv=2×10^11,即没有移动的距离限制,每个城市都能到达它的所有祖先;
  当 t=3 时,数据没有特殊性质。
  n=2×10^5。

Solution

  看上去觉得可做系列。

  首先很容易写出答案的DP转移方程:

  设S[x]为节点x到根的距离,其中j满足j为i的祖先且S[i]-S[j]<=l[i]。

  既然是一个1D-1D的方程,我们暂时考虑斜率优化的做法。

  经过数学推导,得到不等式。当j为k的祖先时,若从j转移至i比从k转移更优,则有

    

  所以就相当于让每一个可以转移到点i的节点j,对应平面上的一个点(S[j],f[j]),我们要找到一个点x满足:

    对于每一个点y为x的祖先,都有

    对于每一个点y为x的子孙,都有

  如下图,红线的斜率为d[i],则从编号为x的点转移最优。

    

  其实最优转移点就是红线从右下角扫描过来时第一个碰到的点。

  于是我们发现第一个碰到的点肯定位于这些点的凸包上,所以我们只要求出凸包,将斜率在凸包上二分找最优转移点即可。

  由于红线的斜率即d[i]为自然数,我们只需维护半边凸包。

    

  所以,我们只要得到一段区间上的凸包,就能得到答案,我们选择大力树套树维护凸包。

  (用线段树的的每一个节点表示从当前点到根节点的链上 该线段树的节点所表示的区间上 的凸包)

  由于是在一棵树上进行维护,所以从当前点到根节点的链(其实是一个栈)是会改变的,有进栈和出栈操作。

  由于有出栈操作,可持久化显然会MLE,我们就必须支持撤销操作。

  小C写的是zkw线段树套线段树,当然还有更优更简单的zkw套无旋Treap做法。(小C作死后面会提到)

  无旋Treap就是每次存下split掉的那 logn 棵树(的根),撤销时merge回来即可。

  线段树就是每次存下每次(最多logn次)删掉的那个节点的右子树的编号,撤销时补回来即可。(如下图)(空间复杂度预警)

   

  如果更新凸包时,新的节点插入于红色框处,那么绿色框就要被删去,即将其父亲的右儿子指向0即可。

  线段树需要动态开点,这样通过数学推导,空间只有O(nlogn)。

  每棵线段树的根节点的范围还要根据 zkw线段树的节点 对应的区间的宽度 来定,否则会被卡常。

  (读者:TMD线段树要注意的东西那么多还不如不说。 小C:人家打了好久的就是要说你来打我啊)

  总时间复杂度

  这题似乎还有很巧妙的cdq分治算法。

  每次像点分治那样做,然后用包含根的那个子树更新其它子树。

    

  如图,红色点为重心,用绿色链来更新黄色部分子树的答案。

  将每个黄色部分内的点按照能爬到绿色链内的深度排序,将绿色链内依次加入凸包时就可以顺便更新答案了……你懂的。

  对于每一个分治结构都这样做,总复杂度O(nlogn)。

  1. #include <cstdio>
  2. #include <algorithm>
  3. #define l(a) (son[a][0])
  4. #define r(a) (son[a][1])
  5. #define ll long long
  6. #define INF 1LL<<62
  7. #define MM 7400005
  8. #define MN 200005
  9. #define MS 19
  10. using namespace std;
  11. struct node
  12. {
  13. int pos; double slope;
  14. friend node operator+(const node& a,const node& b){return b.pos?b:(node){,};}
  15. }t[MM],g[MN][MS];
  16. struct edge{int nex,to; ll wt;}e[MN];
  17. int pin,din,n,tp,MO;
  18. int hr[MN],a[MN],rt[MN*],st[MN],son[MM][];
  19. int cg[MN][MS][MS],bh[MN][MS];
  20. ll f[MN],s[MN],lim[MN],b[MN];
  21.  
  22. inline ll read()
  23. {
  24. ll n=,f=; char c=getchar();
  25. while (c<'' || c>'') {if(c=='-')f=-; c=getchar();}
  26. while (c>='' && c<='') {n=n*+c-''; c=getchar();}
  27. return n*f;
  28. }
  29. inline void ins(int x,int y,ll z) {e[++pin]=(edge){hr[x],y,z}; hr[x]=pin;}
  30.  
  31. node Tgetans(int x,int L,int R,int z)
  32. {
  33. if (!x||L==R) return (node){,};
  34. int mid=L+R>>;
  35. if (!t[l(x)].pos||z<t[l(x)].slope) return Tgetans(l(x),L,mid,z);
  36. else {node lt=Tgetans(r(x),mid+,R,z); return lt.pos?lt:t[l(x)];}
  37. }
  38. void Tmodify(int& x,int L,int R,int y,int z,int pre,int depth)
  39. {
  40. if (!x) x=++din;
  41. if (L==R) {g[y][z]=t[x]; bh[y][z]=L; t[x]=(node){y,pre?(double)(f[y]-f[pre])/(s[y]-s[pre]):}; return;}
  42. int mid=L+R>>;
  43. if (t[l(x)].pos&&(double)(f[y]-f[t[l(x)].pos])/(s[y]-s[t[l(x)].pos])>t[l(x)].slope) Tmodify(r(x),mid+,R,y,z,t[l(x)].pos,depth+);
  44. else {Tmodify(l(x),L,mid,y,z,pre,depth+); cg[y][z][depth]=r(x); r(x)=;}
  45. t[x]=t[l(x)]+t[r(x)];
  46. }
  47. void Tcance(int x,int L,int R,int y,int z,int depth)
  48. {
  49. if (L==R) {t[x]=g[y][z]; return;}
  50. int mid=L+R>>;
  51. if (bh[y][z]>mid) Tcance(r(x),mid+,R,y,z,depth+);
  52. else {Tcance(l(x),L,mid,y,z,depth+); r(x)=cg[y][z][depth];}
  53. t[x]=t[l(x)]+t[r(x)];
  54. }
  55.  
  56. void getans(int x,int ql,int qr,int z)
  57. {
  58. node lt; f[x]=INF;
  59. register int j;
  60. for (j=,ql+=MO,qr+=MO;ql<=qr;ql>>=,qr>>=,j<<=)
  61. {
  62. if ( ql&)
  63. {
  64. if (t[rt[ql]].pos&&z>=t[rt[ql]].slope) lt=t[rt[ql++]];
  65. else lt=Tgetans(rt[ql++],,j,z);
  66. f[x]=min(f[x],f[lt.pos]+(s[x]-s[lt.pos])*z+b[x]);
  67. }
  68. if (~qr&)
  69. {
  70. if (t[rt[qr]].pos&&z>=t[rt[qr]].slope) lt=t[rt[qr--]];
  71. else lt=Tgetans(rt[qr--],,j,z);
  72. f[x]=min(f[x],f[lt.pos]+(s[x]-s[lt.pos])*z+b[x]);
  73. }
  74. }
  75. }
  76. void modify(int x,int q)
  77. {
  78. register int i,j;
  79. for (i=,j=,q+=MO;q;q>>=,++i,j<<=) Tmodify(rt[q],,j,x,i,,);
  80. }
  81. void cance(int x,int q)
  82. {
  83. register int i,j;
  84. for (i=,j=,q+=MO;q;q>>=,++i,j<<=) Tcance(rt[q],,j,x,i,);
  85. }
  86.  
  87. void dfs(int x)
  88. {
  89. register int i,L,R;
  90. for (L=,R=tp;L<R;)
  91. {
  92. int mid=L+R>>;
  93. if (s[x]-s[st[mid]]>lim[x]) L=mid+; else R=mid;
  94. }
  95. if (R) getans(x,R,tp,a[x]);
  96. st[++tp]=x; modify(x,tp);
  97. for (i=hr[x];i;i=e[i].nex)
  98. {
  99. s[e[i].to]=s[x]+e[i].wt;
  100. dfs(e[i].to);
  101. }
  102. cance(x,tp); --tp;
  103. }
  104.  
  105. int main()
  106. {
  107. register int i,x;
  108. register ll y;
  109. n=read(); x=read();
  110. for (MO=;MO<n;MO<<=); --MO;
  111. for (i=;i<=n;++i)
  112. {
  113. x=read(); y=read(); ins(x,i,y);
  114. a[i]=read(); b[i]=read(); lim[i]=read();
  115. }
  116. dfs();
  117. for (i=;i<=n;++i) printf("%lld\n",f[i]);
  118. }

Last Word

  这大概是小C唯一一次那么执着地要把一种自己想的做法写完的题目了。(没办法小C强迫症)

  刚开始看到题目时忽略了有距离限制的条件,果断就打了线段树维护凸包上去,结果打完才发现GG。

  看错题面癌持续发作的小C之后又卯足劲向南墙撞去……至于写了什么丢人的代码这里就不说了。

  最后发现要求出区间凸包只有树套树一条路,线段树套平衡树的做法很显然……

  但小C觉得既然只是维护一个栈,就只有修改一个点和删除一个后缀的操作,线段树似乎也可以完成?

  于是抱着尝试的心态写了个可持久化线段树,发现MLE之后,小C才真真正正地被恶心到了。

  然后就写了撤销操作,通过计算发现动态开点空间十分科学。然后交上去跑了3.5秒TLE……Excuse me?

  仔细想想只有zkw的1号节点才要开[1,n],而2、3号节点只要开[1,n/2]?

  然后就过了……小C作个死还这么难啊……

  发现小C写的zkw跟《统计的力量》里面写的有些不同诶,人家是开区间而小C是闭区间。

  最后小D太强啦,本一线上清华啦,orz ditoly。

[BZOJ]3672 购票(Noi2014)的更多相关文章

  1. LOJ 2249: 洛谷 P2305: bzoj 3672: 「NOI2014」购票

    题目传送门:LOJ #2249. 题意简述: 有一棵以 \(1\) 号节点为根节点的带边权的树. 除了 \(1\) 号节点的所有节点上都有人需要坐车到达 \(1\) 号节点. 除了 \(1\) 号节点 ...

  2. bzoj 3672 购票 点分治+dp

    3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1177  Solved: 562[Submit][Status][ ...

  3. BZOJ 3672 购票

    Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国\(n\)个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与 ...

  4. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  5. bzoj 3672: [Noi2014]购票 树链剖分+维护凸包

    3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 480  Solved: 212[Submit][Status][D ...

  6. BZOJ 3672: [Noi2014]购票( 树链剖分 + 线段树 + 凸包 )

    s弄成前缀和(到根), dp(i) = min(dp(j) + (s(i)-s(j))*p(i)+q(i)). 链的情况大家都会做...就是用栈维护个下凸包, 插入时暴力弹栈, 查询时就在凸包上二分/ ...

  7. 【BZOJ 3672】 3672: [Noi2014]购票 (CDQ分治+点分治+斜率优化)**

    3672: [Noi2014]购票 Description  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.        全国 ...

  8. ●BZOJ 3672 [Noi2014]购票

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3672 题解: 斜率优化DP,点分治(树上CDQ分治...) 这里有一个没有距离限制的简单版: ...

  9. BZOJ 3672 [Noi2014]购票 (熟练剖分+凸壳维护)

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3672 题意:给出一棵有根树(1为根),边有长度.每个点u有三个属性(len[u], ...

随机推荐

  1. nyoj 阶乘0

    阶乘的0 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 计算n!的十进制表示最后有多少个0   输入 第一行输入一个整数N表示测试数据的组数(1<=N< ...

  2. 第一次制作和使用图标字体-IcoMoon

    开题:之前就有所耳闻,最近两天第一次运用到图标字体.刚开始嘛,一脸懵逼的状态.成功运用之后就来记录一下使用过程咯! 1. 打开在线生成工具:https://icomoon.io/app/#/selec ...

  3. mysql常用命令整理

    #不压缩备份 mysqldump -u root -p userpassword databasename > /tmp/backupfile.sql #压缩备份 mysqldump -u ro ...

  4. Python内置函数(57)——print

    英文文档: print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) Print objects to the text str ...

  5. Python内置函数(55)——globals

    英文文档: globals() Return a dictionary representing the current global symbol table. This is always the ...

  6. angluarjs2入门学习资源

    http://www.runoob.com/angularjs2/angularjs2-tutorial.htmlhttps://segmentfault.com/a/1190000008423981 ...

  7. linux下xargs和管道的区别

    管道将前面的标准输出作为后面的标准输入,xargs则将标准输入作为命令的参数 一.简介 1.背景 之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了x ...

  8. maven快速下载jar镜像

    <!--国内镜像--><mirror>  <id>CN</id>  <name>OSChina Central</name>  ...

  9. Python3安装Requests

    安装Requests费了1天的时间,囧.终于还是在官网找到解决方法,可以参考这个http://docs.python-requests.org/en/latest/user/install/#inst ...

  10. 转:java中Vector的使用

    转:https://www.cnblogs.com/zhaoyan001/p/6077492.html Vector 可实现自动增长的对象数组. java.util.vector提供了向量类(vect ...