前言

刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长。


BZOJ 3672[NOI2014]购票

  • 中文题面,题意略: BZOJ 3672[NOI2014]购票
  • 设f(i)f(i)f(i)表示iii点所花的最小费用,可以写出方程式f(i)=min{ f(j)+pi(disi−disj)+qi }f(i)=min\{\ f(j)+p_i(dis_i-dis_j)+q_i\ \}f(i)=min{ f(j)+pi​(disi​−disj​)+qi​ }其中jjj是iii的祖先且 disi−disj&lt;=lidis_i-dis_j&lt;=l_idisi​−disj​<=li​
  • 显然可以斜率优化。那么我们来想想如何在树上做斜率优化。方法就是树链剖分后用dfsdfsdfs序建一颗线段树。线段树的每一个节点上用vectorvectorvector维护这个区间中所有点形成的下凸包。从根往下DPDPDP,在DPDPDP同时维护一个栈来存从根到当前点uuu的链上的点,这些点都有可能转移到当前点。那么只需要在这条链上二分出深度最小且满足 disi−disj&lt;=lidis_i-dis_j&lt;=l_idisi​−disj​<=li​ 的点vvv,在线段树中[dfnv,dfnu][dfn_v,dfn_u][dfnv​,dfnu​]的所有凸包里,二分查询答案就行。树链剖分+线段树+凸包二分,时间复杂度O(nlog3n)O(nlog^3n)O(nlog3n)。
  • CODE

    1. #include <bits/stdc++.h>
    2. using namespace std;
    3. typedef long long LL;
    4. const int MAXN = 200005;
    5. const LL inf = 1e18;
    6. int n, fa[MAXN], S[MAXN], indx;
    7. int fir[MAXN], to[MAXN], w[MAXN], nxt[MAXN], cnt;
    8. LL dis[MAXN], f[MAXN], P[MAXN], Q[MAXN], L[MAXN];
    9. vector<int> t[MAXN<<2];
    10. int sz[MAXN], hson[MAXN], top[MAXN], dfn[MAXN], seq[MAXN], tmr;
    11. template<typename T>inline void read(T &num) {
    12. char ch; while((ch=getchar())<'0'||ch>'9');
    13. for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
    14. }
    15. inline void Addedge(int u, int v, int wt) {
    16. to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; w[cnt] = wt;
    17. }
    18. inline void dfs(int u) {
    19. sz[u] = 1;
    20. for(int i = fir[u]; i; i = nxt[i]) {
    21. dis[to[i]] = dis[u] + w[i];
    22. dfs(to[i]); sz[u] += sz[to[i]];
    23. if(sz[to[i]] > sz[hson[u]]) hson[u] = to[i];
    24. }
    25. }
    26. inline void dfs2(int u, int tp) {
    27. top[u] = tp; dfn[u] = ++tmr; seq[tmr] = u;
    28. if(hson[u]) dfs2(hson[u], tp);
    29. for(int i = fir[u]; i; i = nxt[i])
    30. if(to[i] != hson[u]) dfs2(to[i], to[i]);
    31. }
    32. inline double Slope(int i, int j) {
    33. return (double)(f[i] - f[j]) / (dis[i] - dis[j]);
    34. }
    35. inline void Modify(int i, int l, int r, int x) {
    36. int sz = t[i].size();
    37. while(sz > 1 && Slope(seq[x], t[i][sz-2]) < Slope(t[i][sz-1], t[i][sz-2]))
    38. t[i].pop_back(), --sz;
    39. t[i].push_back(seq[x]);
    40. if(l == r) return;
    41. int mid = (l + r) >> 1;
    42. if(x <= mid) Modify(i<<1, l, mid, x);
    43. else Modify(i<<1|1, mid+1, r, x);
    44. }
    45. inline LL calc(vector<int>t, int i) {
    46. int l = 1, r = t.size()-1, mid, pos = 0;
    47. while(l <= r) {
    48. mid = (l + r + 1) >> 1;
    49. if(Slope(t[mid], t[mid-1]) < 1.0*P[i]) pos = mid, l = mid+1;
    50. else r = mid-1;
    51. }
    52. int j = t[pos];
    53. return f[j] + (dis[i]-dis[j])*P[i] + Q[i];
    54. }
    55. inline LL Query(int i, int l, int r, int x, int y, int id) {
    56. if(x <= l && r <= y) return calc(t[i], id);
    57. int mid = (l + r) >> 1; LL res = inf;
    58. if(x <= mid) res = min(res, Query(i<<1, l, mid, x, y, id));
    59. if(y > mid) res = min(res, Query(i<<1|1, mid+1, r, x, y, id));
    60. return res;
    61. }
    62. inline void solve(int i, int anc) {
    63. int u = fa[i]; f[i] = inf;
    64. while(top[u] != top[anc])
    65. f[i] = min(f[i], Query(1, 1, n, dfn[top[u]], dfn[u], i)), u = fa[top[u]];
    66. f[i] = min(f[i], Query(1, 1, n, dfn[anc], dfn[u], i));
    67. }
    68. inline void dp(int u) {
    69. S[++indx] = u;
    70. if(u > 1) {
    71. int l = 1, r = indx-1, mid;
    72. while(l < r) {
    73. mid = (l + r) >> 1;
    74. if(dis[u] - dis[S[mid]] <= L[u]) r = mid;
    75. else l = mid+1;
    76. }
    77. solve(u, S[l]);
    78. }
    79. Modify(1, 1, n, dfn[u]);
    80. for(int i = fir[u]; i; i = nxt[i]) dp(to[i]);
    81. --indx;
    82. }
    83. int main () {
    84. int type;
    85. read(n), read(type);
    86. for(int i = 2, x; i <= n; ++i) {
    87. read(fa[i]), read(x), read(P[i]), read(Q[i]), read(L[i]);
    88. Addedge(fa[i], i, x);
    89. }
    90. dfs(1); dfs2(1, 1); dp(1);
    91. for(int i = 2; i <= n; ++i)
    92. printf("%lld\n", f[i]);
    93. }

BZOJ 2402 陶陶的难题II

  • 题意略: 2402: 陶陶的难题II
  • 想想在长度为nnn序列上如何求这个最大值。暴力是O(n2)O(n^2)O(n2)的。

    设最终得到最大比值为bestbestbest。那么就有yi+qjxi+pi&lt;=bestyi+qj&lt;=best∗xi+best∗pi(yi−best∗xi)+(qj−best∗pj)&lt;=0\large\begin{aligned}\frac{y_i+q_j}{x_i+p_i}&amp;&lt;=best\\ y_i+q_j&amp;&lt;=best*x_i+best*p_i\\(y_i-best*x_i)+(q_j-best*p_j)&amp;&lt;=0\end{aligned}xi​+pi​yi​+qj​​yi​+qj​(yi​−best∗xi​)+(qj​−best∗pj​)​<=best<=best∗xi​+best∗pi​<=0​对任意i,ji,ji,j都满足,且存在至少一组i,ji,ji,j使等式取等。
  • 设等式左边的最大值为fff,那么对于任意取值best′best'best′,有f(best′)&lt;0,    best′&gt;bestf(best′)=0,    best′=bestf(best′)&gt;0,    best′&lt;best\begin{aligned}f(best')&lt;0,&amp;\ \ \ \ best'&gt;best\\f(best')=0,&amp;\ \ \ \ best'=best\\f(best')&gt;0,&amp;\ \ \ \ best'&lt;best\end{aligned}f(best′)<0,f(best′)=0,f(best′)>0,​    best′>best    best′=best    best′<best​
  • 显然我们可以二分,每次求最大值fff就行了。那么等式左边的最大值只用分别算(yi−best∗xi)(y_i-best*x_i)(yi​−best∗xi​)和(qj−best∗pj)(q_j-best*p_j)(qj​−best∗pj​)的最大值再加起来。计算最大值时就是用斜率优化,维护一个上凸包。在凸包上二分求最值就行了。
  • 而转移到树上就像上一道题一样在dfsdfsdfs序上用线段树维护凸包就行了。
  • 这道题buildbuildbuild线段树时需要把子树上传来的两个凸包合并,注意往凸包里加点时要保证xxx坐标递增。
  • 时间复杂度O(nlog4n)O(nlog^4n)O(nlog4n),能过真是奇迹(听说树链跑不满+凸包上的点少?)
  • CODE

    1. #include <bits/stdc++.h>
    2. using namespace std;
    3. typedef long long LL;
    4. const int MAXN = 300005;
    5. const double inf = 1e16;
    6. const double eps = 1e-10;
    7. int n; double x[MAXN][2], y[MAXN][2];
    8. int to[MAXN<<1], nxt[MAXN<<1], fir[MAXN], cnt;
    9. int sz[MAXN], top[MAXN], fa[MAXN], hson[MAXN], dfn[MAXN], tmr, seq[MAXN], dep[MAXN];
    10. inline void read(int &num) {
    11. char ch; while((ch=getchar())<'0'||ch>'9');
    12. for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
    13. }
    14. inline void addedge(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; }
    15. void dfs(int u, int ff) {
    16. fa[u] = ff; sz[u] = 1;
    17. dep[u] = dep[fa[u]] + 1;
    18. for(int i = fir[u]; i; i = nxt[i])
    19. if(to[i] != fa[u]) {
    20. dfs(to[i], u), sz[u] += sz[to[i]];
    21. if(sz[to[i]] > sz[hson[u]]) hson[u] = to[i];
    22. }
    23. }
    24. void dfs2(int u, int tp) {
    25. top[u] = tp; dfn[u] = ++tmr; seq[tmr] = u;
    26. if(hson[u]) dfs2(hson[u], tp);
    27. for(int i = fir[u]; i; i = nxt[i])
    28. if(to[i] != fa[u] && to[i] != hson[u])
    29. dfs2(to[i], to[i]);
    30. }
    31. struct SegmentTree {
    32. vector<int> vec[MAXN<<2]; bool flg;
    33. inline double slope(const int &i, const int &j) {
    34. return (y[i][flg]-y[j][flg]) / (x[i][flg]-x[j][flg]);
    35. }
    36. inline bool Turn_left(const int &i, const int &j, const int &k) {
    37. register double a = x[j][flg] - x[i][flg], b = y[j][flg] - y[i][flg];
    38. register double c = x[k][flg] - x[i][flg], d = y[k][flg] - y[i][flg];
    39. return a*d + eps > b*c;
    40. }
    41. inline void Merge(vector<int> &h, const vector<int> &h1, const vector<int> &h2) {
    42. vector<int>::const_iterator i, j;
    43. i = h1.begin(), j = h2.begin();
    44. int top = 0;
    45. while(i != h1.end() || j != h2.end()) { //下面的比较大小就是保证x递增
    46. int p = i == h1.end() ? *j++ : j == h2.end() ? *i++ : x[*i][flg] < x[*j][flg] ? *i++ : *j++;
    47. while(top >= 2 && Turn_left(h[top-2], h[top-1], p)) h.pop_back(), --top;
    48. h.push_back(p), ++top;
    49. }
    50. }
    51. void build(int i, int l, int r) {
    52. if(l == r) { vec[i].push_back(seq[l]); return; }
    53. register int mid = (l + r) >> 1;
    54. build(i<<1, l, mid);
    55. build(i<<1|1, mid+1, r);
    56. Merge(vec[i], vec[i<<1], vec[i<<1|1]);
    57. }
    58. inline double calc(const vector<int> &V, const double &now) {
    59. register int l = 1, r = V.size()-1, mid, pos = V[0];
    60. while(l <= r) {
    61. mid = (l + r) >> 1;
    62. if(slope(V[mid-1], V[mid]) + eps > now) pos = V[mid], l = mid+1;
    63. else r = mid-1;
    64. }
    65. return y[pos][flg] - now*x[pos][flg];
    66. }
    67. double Query(const int &i, const int &l, const int &r, const int &L, const int &R, const double &now) {
    68. if(L <= l && r <= R) return calc(vec[i], now);
    69. int mid = (l + r) >> 1; register double res = -inf;
    70. if(L <= mid) res = max(res, Query(i<<1, l, mid, L, R, now));
    71. if(R > mid) res = max(res, Query(i<<1|1, mid+1, r, L, R, now));
    72. return res;
    73. }
    74. }T[2];
    75. inline double check(int x, int y, const double &now, const bool &flg) {
    76. register double res = -inf;
    77. register int fx = top[x], fy = top[y];
    78. while(fx != fy) {
    79. if(dep[fx] < dep[fy]) swap(x, y), swap(fx, fy);
    80. res = max(res, T[flg].Query(1, 1, n, dfn[top[x]], dfn[x], now));
    81. x = fa[fx], fx = top[x];
    82. }
    83. if(dep[x] < dep[y]) swap(x, y);
    84. res = max(res, T[flg].Query(1, 1, n, dfn[y], dfn[x], now));
    85. return res;
    86. }
    87. inline int lca(int u, int v) {
    88. while(top[u] != top[v]) {
    89. if(dep[top[u]] > dep[top[v]]) u = fa[top[u]];
    90. else v = fa[top[v]];
    91. }
    92. return dep[u] > dep[v] ? v : u;
    93. }
    94. inline bool cmp0(const int &i, const int &j) { return x[i][0] < x[j][0]; }
    95. inline bool cmp1(const int &i, const int &j) { return x[i][1] < x[j][1]; }
    96. inline int dcmp(double x) {
    97. if(fabs(x) < eps) return 0;
    98. if(x > 0) return 1;
    99. return -1;
    100. }
    101. int main () {
    102. read(n); T[1].flg = 1;
    103. for(int i = 1; i <= n; ++i) scanf("%lf", &x[i][0]);
    104. for(int i = 1; i <= n; ++i) scanf("%lf", &y[i][0]);
    105. for(int i = 1; i <= n; ++i) scanf("%lf", &x[i][1]);
    106. for(int i = 1; i <= n; ++i) scanf("%lf", &y[i][1]);
    107. for(int i = 1, a, b; i < n; ++i)
    108. read(a), read(b), addedge(a, b), addedge(b, a);
    109. dfs(1, 0); dfs2(1, 1);
    110. T[0].build(1, 1, n);
    111. T[1].build(1, 1, n);
    112. int m, a, b;
    113. read(m);
    114. while(m--){
    115. read(a), read(b);
    116. double l = 0, r = 1e8, mid;
    117. while(r - l > 1e-5) {
    118. mid = (l + r) / 2;
    119. if(dcmp(check(a, b, mid, 0) + check(a, b, mid, 1)) >= 0) l = mid;
    120. else r = mid;
    121. }
    122. printf("%.5f\n", l);
    123. }
    124. }
  • 本人是大常数选手,37192ms37192ms37192ms卡过40000ms40000ms40000ms。

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

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

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

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

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

  3. BZOJ 3672 [NOI2014]购票 (凸优化+树剖/树分治)

    题目大意: 略 题面传送门 怎么看也是一道$duliu$题= = 先推式子,设$dp[x]$表示到达$x$点到达1节点的最小花费 设$y$是$x$的一个祖先,则$dp[x]=min(dp[y]+(di ...

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

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

  5. ●BZOJ 3672 [Noi2014]购票

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

  6. BZOJ 3672: [Noi2014]购票 树上CDQ分治

    做这道题真的是涨姿势了,一般的CDQ分治都是在序列上进行的,这次是把CDQ分治放树上跑了~ 考虑一半的 CDQ 分治怎么进行: 递归处理左区间,处理左区间对右区间的影响,然后再递归处理右区间. 所以, ...

  7. bzoj 3672: [Noi2014]购票

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

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

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

  9. bzoj千题计划251:bzoj3672: [Noi2014]购票

    http://www.lydsy.com/JudgeOnline/problem.php?id=3672 法一:线段树维护可持久化单调队列维护凸包 斜率优化DP 设dp[i] 表示i号点到根节点的最少 ...

随机推荐

  1. SAS学习笔记26 方差分析

    对于多于两组(k>2)样本均数的比较,t检验不再适用,方差分析(analysis of variance, ANOVA)则是解决上述问题的重要分析方法.方差分析由R.A.Fisher(1923) ...

  2. Merkle树

    在分布式系统.P2P应用中或者是区块链中,会经常使用一种数据结构Merkle tree(默克尔树),这里我们将详细讨论一下这个常用数据结构. Merkle tree Merkle树看起来非常像二叉树, ...

  3. sql server 学习笔记 (nested transaction 嵌套事务)

    什么时候会用到嵌套事务 ? 为了代码复用,我们会写许多的储蓄过程,而中间如果需要使用到 transaction 难免就会发生嵌套了. sql server 并不直接支持嵌套事务. 但它可以用一些招式来 ...

  4. Centos7.3 为php7 安装swoole 扩展

    今天心血来潮想在服务器上安装一下swoole扩展  下面列一下教程: xshell进入你的服务器  然后目录自选吧  反正我放在根目录了 下面是扩展链接: wget https://github.co ...

  5. (一)easyUI之树形网络

    树形网格(TreeGrid)可以展示有限空间上带有多列和复杂数据电子表 一.案例一:按tree的数据结构来生成 前台 <%@ page language="java" con ...

  6. CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败

    今天在使用 C# 操作 Excel 时,一直在报错误: 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下 ...

  7. java封装数据类型——Long

    Long 是长整型 long 的封装数据类型.我们知道 long 相对于 int 的差异就是数据表示的范围扩大了,其它大部分特性都是一样的.所以 Long 跟 Integer 大部分方法都是相同的. ...

  8. POJ2945(Find the Clones)--字典树,map

    题意:给你n个规定长度的单词,问你其中出现了1次的单词,出现两次的单词...出现n次单词分别有多少个. 当然这题map也能过,但是这里介绍字典树的做法. 首相对于n个单词存入树中,当然建树过程中遇到一 ...

  9. 1 vue 关键字解释

    1 每一个计算属性都包含一个getter和一个setter,计算属性可以依赖其他计算属性,计算属性可以依赖当前vue实例的数据也可以依赖其他vue实例的数据 2 计算属性是基于它的依赖缓存的,方法则是 ...

  10. ant design pro超详细入门教程

    1.Ant Design Pro 初了解 说到ant design pro,得先了解一下ant design是个什么东西?ant design蚂蚁金服基于react打造的一个服务于企业级产品的UI框架 ...