http://www.lydsy.com/JudgeOnline/problem.php?id=3672

法一:线段树维护可持久化单调队列维护凸包 斜率优化DP

设dp[i] 表示i号点到根节点的最少花费

dis[i] 表示 点i到根节点的距离

dp[i]= min { (dis[i]-dis[j])* P[i] + Q[i] + dp[j] }   j是i的祖先且dis[i]-dis[j]<=L[i]

即 dp[i]+dis[j]*P[i]=dp[j]+dis[i]*P[i]+Q[i]

斜率优化,dp[i]=斜率为P[i] 的直线 截(dis[j],dp[j])所得的截距最小值

如果不考虑L[i] 的限制:

那就可以用一个可持久化的单调队列维护 根节点到当前点的路径上所有的点构成的下凸壳

考虑如何去除兄弟节点的子树对单调队列的影响

即在一个节点退出dfs时,将单调队列恢复为这个节点开始dfs的情况

头指针:头指针的前移不会涉及单调队列元素的修改,因为从根往下走不能保证斜率单调,所以头指针不能出队,需要二分找到最小的 斜率>P[i] 的点来更新dp[i]

尾指针:尾指针的前移会涉及到单调队列元素的替换,但替换只会替换一个元素,所以记录下 替换的是谁,原来的尾指针是谁,退出的时候恢复即可

加上L[i]的限制:

用线段树维护,若线段树节点的区间为[l,r],那这个节点维护的是当前链上  深度为[l,r]的点构成的凸包

即由原来的维护一个单调队列变成维护nlogn个单调队列

每个节点开一个vector? 光开nlogn个vector就TLE了吧

开一个nlogn的数组v存储整条链上凸包内的点

在建树的时候,按节点的顺序给头指针分一个位置x,若节点的大小为y,

那么这个节点维护的单调队列 中的点会出现在v数组的[x,x+y-1]位置

这样线段树内只需要存储单调队列的头尾指针即可

单调队列采用 左闭右开 的好处:

多个可持久化单调队列,头尾指针相连

如果头尾指针是左闭右闭

如果队列为空,tail<head,导致 后一个队列的尾指针=前一个队列的头指针

这样尾指针替换更改,记录的替换位置为tail,即前一个队列的head

退出dfs替换回去 错误的替换了前一个队列的head

头尾指针左闭右开就不会有这个问题

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4.  
  5. using namespace std;
  6.  
  7. #define N 200001
  8.  
  9. typedef long long LL;
  10.  
  11. int n;
  12. int P[N];
  13. LL Q[N],L[N];
  14.  
  15. int front[N],to[N],nxt[N],tot;
  16. LL val[N];
  17.  
  18. struct node
  19. {
  20. int head,tail;
  21. }tr[N<<];
  22. int v[N*];
  23.  
  24. int sum;
  25.  
  26. LL dep[N],dis[N];
  27. LL dp[N];
  28.  
  29. int re_t[N][],re_w[N][];
  30.  
  31. template<typename T>
  32. void read(T &x)
  33. {
  34. x=; char c=getchar();
  35. while(!isdigit(c)) c=getchar();
  36. while(isdigit(c)) { x=x*+c-''; c=getchar(); }
  37. }
  38.  
  39. void add(int u,int v,LL w)
  40. {
  41. to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
  42. }
  43.  
  44. void build(int k,int l,int r)
  45. {
  46. tr[k].head=sum+;
  47. tr[k].tail=tr[k].head;
  48. sum+=r-l+;
  49. if(l==r) return;
  50. int mid=l+r>>;
  51. build(k<<,l,mid);
  52. build(k<<|,mid+,r);
  53. }
  54.  
  55. inline double slope(int i,int j)
  56. {
  57. return 1.0*(dp[j]-dp[i])/(dis[j]-dis[i]);
  58. }
  59.  
  60. int find_ans(int k,int p)
  61. {
  62. int l=tr[k].head;
  63. int r=tr[k].tail-;
  64. if(l==r) return v[l];
  65. r--;
  66. int mid,tmp=-;
  67. while(l<=r)
  68. {
  69. mid=l+r>>;
  70. if(p<slope(v[mid],v[mid+])) tmp=mid,r=mid-;
  71. else l=mid+;
  72. }
  73. if(tmp==-) return v[tr[k].tail-];
  74. return v[tmp];
  75. }
  76.  
  77. int query(int k,int l,int r,int opl,int opr,int p)
  78. {
  79. if(l>=opl && r<=opr) return find_ans(k,p);
  80. int mid=l+r>>;
  81. if(opr<=mid) return query(k<<,l,mid,opl,opr,p);
  82. else if(opl>mid) return query(k<<|,mid+,r,opl,opr,p);
  83. else
  84. {
  85. int t1=query(k<<,l,mid,opl,opr,p);
  86. int t2=query(k<<|,mid+,r,opl,opr,p);
  87. if(p<slope(t1,t2)) return t1;
  88. return t2;
  89. }
  90. }
  91.  
  92. int find(int r,LL lim)
  93. {
  94. int l=;
  95. int tmp,mid;
  96. while(l<=r)
  97. {
  98. mid=l+r>>;
  99. if(dep[mid]>=lim) tmp=mid,r=mid-;
  100. else l=mid+;
  101. }
  102. return tmp;
  103. }
  104.  
  105. int in_queue(int k,int j)
  106. {
  107. if(tr[k].head==tr[k].tail) return tr[k].head;
  108. int l=tr[k].head,r=tr[k].tail-,tmp=-,mid;
  109. while(l<=r)
  110. {
  111. mid=l+r>>;
  112. if(slope(v[mid],v[mid+])>slope(v[mid],j)) tmp=mid+,r=mid-;
  113. else l=mid+;
  114. }
  115. if(tmp==-) return tr[k].tail;
  116. return tmp;
  117. }
  118.  
  119. void insert(int k,int l,int r,int x,int w,int d)
  120. {
  121. int pos=in_queue(k,w);
  122. re_t[w][d]=tr[k].tail;
  123. re_w[w][d]=v[pos];
  124. v[pos]=w;
  125. tr[k].tail=pos+;
  126. if(l==r) return;
  127. int mid=l+r>>;
  128. if(x<=mid) insert(k<<,l,mid,x,w,d+);
  129. else insert(k<<|,mid+,r,x,w,d+);
  130. }
  131.  
  132. void del(int k,int l,int r,int x,int w,int d)
  133. {
  134. v[tr[k].tail-]=re_w[w][d];
  135. tr[k].tail=re_t[w][d];
  136. if(l==r) return;
  137. int mid=l+r>>;
  138. if(x<=mid) del(k<<,l,mid,x,w,d+);
  139. else del(k<<|,mid+,r,x,w,d+);
  140. }
  141.  
  142. void dfs(int x,int d)
  143. {
  144. if(x!=)
  145. {
  146. LL lim=dis[x]-L[x];
  147. int j=query(,,n,find(d-,lim),d-,P[x]);
  148. dp[x]=(dis[x]-dis[j])*P[x]+Q[x]+dp[j];
  149. }
  150. insert(,,n,d,x,);
  151. for(int i=front[x];i;i=nxt[i])
  152. {
  153. dis[to[i]]=dis[x]+val[i];
  154. dep[d+]=dep[d]+val[i];
  155. dfs(to[i],d+);
  156. }
  157. del(,,n,d,x,);
  158. }
  159.  
  160. int main()
  161. {
  162. int t;
  163. read(n); read(t);
  164. int f; LL s;
  165. for(int i=;i<=n;++i)
  166. {
  167. read(f); read(s); read(P[i]); read(Q[i]); read(L[i]);
  168. add(f,i,s);
  169. }
  170. sum=; build(,,n);
  171. insert(,,n,,,);
  172. dfs(,);
  173. for(int i=;i<=n;++i) cout<<dp[i]<<'\n';
  174. return ;
  175. }

法二、点分治+斜率优化dp

朴素的dp是往上找能更新它的点

这里往下找它能更新的点

具体做法:

找出分治重心后,先 解决重心 的祖先(包括重心)那块儿

然后求出 原树根节点到重心的凸包 更新重心的子树

至于 距离的限制,我们把重心的子树中所有点 按能更新它的 深度最小的城市 从大到小排序

枚举这些点i

构造重心到原树根节点的凸包时,从重心往上依次加点,只加入 深度>= 当前点i深度限制 的点

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4.  
  5. using namespace std;
  6.  
  7. #define N 200001
  8.  
  9. typedef long long LL;
  10.  
  11. int n;
  12. int front[N],nxt[N],to[N],tot;
  13. LL val[N];
  14.  
  15. int fa[N],P[N];
  16. LL Q[N],L[N];
  17.  
  18. LL dis[N];
  19.  
  20. bool vis[N];
  21.  
  22. int root;
  23. int siz[N],mx[N];
  24.  
  25. struct node
  26. {
  27. int id;
  28. LL lim;
  29. }e[N];
  30. int cnt;
  31. int st[N];
  32.  
  33. LL dp[N];
  34.  
  35. template<typename T>
  36. void read(T &x)
  37. {
  38. x=; char c=getchar();
  39. while(!isdigit(c)) c=getchar();
  40. while(isdigit(c)) { x=x*+c-''; c=getchar(); }
  41. }
  42.  
  43. void add(int u,int v,LL w)
  44. {
  45. to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
  46. }
  47.  
  48. void dfs(int x)
  49. {
  50. for(int i=front[x];i;i=nxt[i])
  51. {
  52. dis[to[i]]=dis[x]+val[i];
  53. dfs(to[i]);
  54. }
  55. }
  56.  
  57. void get_siz(int x,int all)
  58. {
  59. siz[x]=; mx[x]=;
  60. for(int i=front[x];i;i=nxt[i])
  61. if(!vis[to[i]])
  62. {
  63. get_siz(to[i],all);
  64. siz[x]+=siz[to[i]];
  65. if(siz[to[i]]>mx[x]) mx[x]=siz[to[i]];
  66. }
  67. mx[x]=max(mx[x],all-siz[x]);
  68. if(mx[x]<mx[root] && siz[x]>) root=x;
  69. }
  70.  
  71. void dfs2(int x)
  72. {
  73. e[++cnt].id=x;
  74. e[cnt].lim=dis[x]-L[x];
  75. for(int i=front[x];i;i=nxt[i])
  76. if(!vis[to[i]]) dfs2(to[i]);
  77. }
  78.  
  79. bool cmp(node p,node q)
  80. {
  81. return p.lim>q.lim;
  82. }
  83.  
  84. double slope(int i,int j)
  85. {
  86. return 1.0*(dp[j]-dp[i])/(dis[j]-dis[i]);
  87. }
  88.  
  89. void solve(int x,int all)
  90. {
  91. if(all==) return;
  92. root=;
  93. get_siz(x,all);
  94. int rt=root;
  95. for(int i=front[root];i;i=nxt[i])
  96. vis[to[i]]=true;
  97. solve(x,all-siz[root]+);
  98. cnt=;
  99. for(int i=front[rt];i;i=nxt[i])
  100. dfs2(to[i]);
  101. sort(e+,e+cnt+,cmp);
  102. int now=rt,top=;
  103. for(int i=;i<=cnt;++i)
  104. {
  105. while(now!=fa[x] && e[i].lim<=dis[now])
  106. {
  107. while(top> && slope(st[top],st[top-])<slope(now,st[top])) top--;
  108. st[++top]=now;
  109. now=fa[now];
  110. }
  111. if(top)
  112. {
  113. int l=,r=top-,mid,tmp=-;
  114. while(l<=r)
  115. {
  116. mid=l+r>>;
  117. if(slope(st[mid],st[mid+])>=P[e[i].id]) tmp=mid+,l=mid+;
  118. else r=mid-;
  119. }
  120. if(tmp==-) tmp=;
  121. dp[e[i].id]=min(dp[e[i].id],(dis[e[i].id]-dis[st[tmp]])*P[e[i].id]+Q[e[i].id]+dp[st[tmp]]);
  122. }
  123. }
  124. for(int i=front[rt];i;i=nxt[i])
  125. solve(to[i],siz[to[i]]);
  126. }
  127.  
  128. int main()
  129. {
  130. int t;
  131. read(n); read(t);
  132. LL s;
  133. for(int i=;i<=n;++i)
  134. {
  135. read(fa[i]); read(s); read(P[i]); read(Q[i]); read(L[i]);
  136. add(fa[i],i,s);
  137. }
  138. dfs();
  139. mx[]=n+;
  140. for(int i=;i<=n;++i) dp[i]=2e17+1e12;
  141. solve(,n);
  142. for(int i=;i<=n;++i) cout<<dp[i]<<'\n';
  143. }

3672: [Noi2014]购票

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 1520  Solved: 768
[Submit][Status][Discuss]

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

bzoj千题计划251:bzoj3672: [Noi2014]购票的更多相关文章

  1. bzoj千题计划300:bzoj4823: [Cqoi2017]老C的方块

    http://www.lydsy.com/JudgeOnline/problem.php?id=4823 讨厌的形状就是四联通图 且左右各连一个方块 那么破坏所有满足条件的四联通就好了 按上图方式染色 ...

  2. bzoj千题计划250:bzoj3670: [Noi2014]动物园

    http://www.lydsy.com/JudgeOnline/problem.php?id=3670 法一:KMP+st表 抽离nxt数组,构成一棵树 若nxt[i]=j,则i作为j的子节点 那么 ...

  3. bzoj千题计划238:bzoj3668: [Noi2014]起床困难综合症

    http://www.lydsy.com/JudgeOnline/problem.php?id=3668 这..一位一位的来就好了呀 #include<cstdio> #include&l ...

  4. bzoj千题计划196:bzoj4826: [Hnoi2017]影魔

    http://www.lydsy.com/JudgeOnline/problem.php?id=4826 吐槽一下bzoj这道题的排版是真丑... 我还是粘洛谷的题面吧... 提供p1的攻击力:i,j ...

  5. bzoj千题计划280:bzoj4592: [Shoi2015]脑洞治疗仪

    http://www.lydsy.com/JudgeOnline/problem.php?id=4592 注意操作1 先挖再补,就是补的范围可以包含挖的范围 SHOI2015 的题 略水啊(逃) #i ...

  6. bzoj千题计划177:bzoj1858: [Scoi2010]序列操作

    http://www.lydsy.com/JudgeOnline/problem.php?id=1858 2018 自己写的第1题,一遍过 ^_^ 元旦快乐 #include<cstdio> ...

  7. bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...

  8. bzoj千题计划304:bzoj3676: [Apio2014]回文串(回文自动机)

    https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include& ...

  9. bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

    http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len ...

随机推荐

  1. 【Luogu1501】Tree(Link-Cut Tree)

    [Luogu1501]Tree(Link-Cut Tree) 题面 洛谷 题解 \(LCT\)版子题 看到了顺手敲一下而已 注意一下,别乘爆了 #include<iostream> #in ...

  2. 【BZOJ2005】【NOI2010】能量采集(莫比乌斯反演,容斥原理)

    [BZOJ2005][NOI2010]能量采集(莫比乌斯反演,容斥原理) 题面 Description 栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量.在这些植物采集能量 ...

  3. 【BZOJ4003】【JLOI2015】城池攻占(左偏树)

    题面 题目描述 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池.这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi ...

  4. [CQOI2009]dance跳舞

    每个人拆成两个点,一个表示接受喜欢的,一个表示不接受喜欢的,(男yes,男no,女yes,女no) 男yes->男no,容量为k:女no->女yes,容量为k 男女喜欢,则男yes-> ...

  5. angular+ionic+cordova(实战项目开发中,持续更新自己学到的和遇到的)

    最近公司开始准备做app了,大佬选择了angular+ionic+corvoda的开发结构,但是对于刚刚才开始对angular才有一点点感觉的我,就像是被一击闷棍敲了,半天没反应过来,emmm,怎么办 ...

  6. lodash源码分析之数组的差集

    外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡. --卡尔维诺<烟云> 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodas ...

  7. 【noip模拟】最小点覆盖

    Time Limit: 1000ms      Memory Limit: 128MB Description 最小点覆盖是指在二分图中,用最小的点集覆盖所有的边.当然,一个二分图的最小点覆盖可能有很 ...

  8. 如何在原生微信小程序中实现数据双向绑定

    官网:https://qiu8310.github.io/minapp/ 作者:Mora 在原生小程序开发中,数据流是单向的,无法双向绑定,但是要实现双向绑定的功能还是蛮简单的! 下文要讲的是小程序框 ...

  9. Cesium解决按住滚轮旋转时进入地下的问题

    viewer.clock.onTick.addEventListener(function () {       setMinCamera()})  var setMinCamera = functi ...

  10. 员工选票系统-java

    Yuangong.java package com.toupiao; public class Yuangong { private String name; private int piao; pu ...