实际上是一个不完美算法……cogs上面A不掉(爆栈啦)。感谢机房大佬PPY的指点,现在也写出来供大家参考参考,理解起来应该是比较简单的一种。

  我们首先get出斜率优化方程:

  \(dp[v] = dis[v] * p[u] - dis[u] * p[u] - q[u] + dp[u] \left ( 0 <= dis[u] - dis[v] <= lim[u] \right )\)

  那么  \(y = dp[v]; x = dis[v]; k = p[u]; \)  我们所要做的就是维护一个下凸包以维护最小的b值。现在有两个本题的难点:首先,转移到\(u\)点的\(v\)点必须是u的祖先,且两者之间相距的距离在一定的限制范围之内。如何满足?我们想到,既然需要维护一定范围内的一个凸包,那么我们可以借助数据结构来完成。线段树可以做到:一个线段树上的节点代表从(l ~ r)这段区间内的节点所构成的凸包,那么任何一个区间都可以由线段树上上的节点凑成,取每一个中间的最大值即可。

  所以我们的线段树所维护的即为一条链上的节点(按深度递增编号)。但还有一个问题没有解决:当我们将一个节点加入凸包的时候我们会为了维护凸包的凸性而弹出一些节点(包括在这个节点的子树都处理完毕之后,该节点自身也要离开凸包,因为它不再属于链上的节点)。那么在处理完之后,我们就希望能够将线段树还原回访问它之前的样子——先想想普通的维护做法:从单调队列的队尾不断弹出元素,直到凸包合法为止。仔细考虑这个过程,我们可以将操作简化为:找到最后一个弹出的元素(a), 意思就是弹出元素(a)后加入当前元素(b)(用(b)替换(a)),再更新队尾指针的指向。所以我们只需要对于每一个节点都维护一下它所弹出的节点&修改的队尾位置就可以了,最后再还原回去。

  以下代码——注意INF大大大大大……就这里调了我三个小时,直到我发现了修改INF读到的值竟然不一样的惊天大秘密……Σ(っ°Д°;)っ

#include <bits/stdc++.h>
using namespace std;
#define maxn 200050
#define lgmaxn 25
#define INF 99999999999999999LL
#define ll long long
#define db double
int n, t;
ll P[maxn], Q[maxn], dp[maxn], dis[maxn], len[maxn];
ll X[maxn], Y[maxn], last = ;
int q[maxn * ], dep[maxn], degree[maxn];
int cnp = , head[maxn], maxx; struct tree
{
int head, tail;
}T[maxn * ]; struct edge
{
int to, last, co;
}E[maxn]; struct node
{
int pret, id;
}S[maxn][lgmaxn]; void add(int u, int v, int w)
{
E[cnp].to = v, E[cnp].last = head[u];
E[cnp].co = w, head[u] = cnp ++;
} db Get_S(int a, int b)
{
ll x1 = X[a], y1 = Y[a], x2 = X[b], y2 = Y[b];
return (db) (y1 - y2) / (db) (x1 - x2);
} void Build(int p, int l, int r)
{
int size = (r - l + );
T[p].head = last + , T[p].tail = last;
last += size; if(l == r) return;
int mid = (l + r) >> ;
Build(p << , l, mid);
Build(p << | , mid + , r);
} void update(int p, int l, int r, int k, int tot, node *a)
{
int h = T[p].head, &t = T[p].tail, pret = T[p].tail;
h += ;
while(h <= t)
{
int mid = (h + t) >> ;
if(Get_S(q[mid - ], q[mid]) >= Get_S(q[mid], k)) t = mid - ;
else h = mid + ;
}
t ++; a[++ tot] = (node) { pret, q[t] };
q[t] = k;
if(l == r) return;
int mid = (l + r) >> ;
if(k <= mid) update(p << , l, mid, k, tot, a);
else update(p << | , mid + , r, k, tot, a);
} int check(int h, int t, int k)
{
int l = h, r = t - , ans = -;
if(l <= r + ) ans = l;
while(l <= r)
{
int mid = (l + r) >> ;
if(Get_S(q[mid], q[mid + ]) <= (db) k) ans = mid + , l = mid + ;
else r = mid - ;
}
return q[ans];
} ll query(int p, ll lim, ll K)
{
int t = T[p].tail, h = T[p].head;
if(dis[q[t]] < lim) return INF;
if(dis[q[h]] >= INF) return INF;
if(dis[q[h]] >= lim && dis[q[t]] < INF)
{
ll x = check(T[p].head, T[p].tail, K);
x = Y[x] - K * X[x];
return x;
}
else return min(query(p << , lim, K), query(p << | , lim, K));
} void rebound(int p, int l, int r, int k, int tot, node *a)
{
q[T[p].tail] = a[tot].id;
T[p].tail = a[tot].pret;
int mid = (r + l) >> ;
if(l == r) return;
if(k <= mid) rebound(p << , l, mid, k, ++ tot, a);
else rebound(p << | , mid + , r, k, ++ tot, a);
} void DFS(int u, int fa, ll co)
{
dep[u] = dep[fa] + ;
ll tem = dis[dep[u] - ] + co;
if(u != ) dp[u] = query((ll) , max((ll) , tem - len[u]), P[u]) + tem * P[u] + Q[u];
else dp[u] = ;
if(degree[u])
{
dis[dep[u]] = u == ? : tem; X[dep[u]] = dis[dep[u]], Y[dep[u]] = dp[u];
update(, , n, dep[u], , S[dep[u]]);
for(int i = head[u]; i; i = E[i].last)
{
int v = E[i].to;
DFS(v, u, E[i].co);
}
rebound(, , n, dep[u], , S[dep[u]]);
X[dep[u]] = Y[dep[u]] = dis[dep[u]] = INF;
}
} int main()
{
scanf("%d%d", &n, &t);
for(int i = ; i <= n; i ++) X[i] = INF, Y[i] = INF, dis[i] = INF;
for(int i = ; i <= n; i ++)
{
int f, x;
scanf("%d%d", &f, &x);
scanf("%lld%lld%lld", &P[i], &Q[i], &len[i]);
add(f, i, x);
degree[f] ++;
}
Build(, , n);
DFS(, , );
for(int i = ; i <= n; i ++)
printf("%lld\n", dp[i]);
return ;
}

【题解】NOI2014购票的更多相关文章

  1. 题解 [NOI2014]购票

    题目传送门 题目大意 有一个 \(n\) 个点的树,每个点有三个值 \(p_u,q_u,l_u\) ,现在可以从 \(u\) 走到点 \(v\) 当且仅当 \(v\) 是 \(u\) 的祖先并且 \( ...

  2. [BZOJ3672][UOJ#7][NOI2014]购票

    [BZOJ3672][UOJ#7][NOI2014]购票 试题描述  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.       ...

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

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

  4. 【BZOJ3672】[Noi2014]购票 树分治+斜率优化

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

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

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

  6. $NOI2014$ 购票(斜率优化 点分治)

    \(NOI2014\)购票 哇终于可以碰电脑了赶快切些火题找找感觉. 拿到这道题的时候发现简单的斜率优化推一推可以秒掉平方做法,然后一条链也可以做. 然后呢... 卧槽这个在一棵树上怎么办啊. 大力\ ...

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

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

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

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

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

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

  10. [BZOJ3672][Noi2014]购票 斜率优化+点分治+cdq分治

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

随机推荐

  1. MySQL 清除表空间碎片

    碎片产生的原因 (1)表的存储会出现碎片化,每当删除了一行内容,该段空间就会变为空白.被留空,而在一段时间内的大量删除操作,会使这种留空的空间变得比存储列表内容所使用的空间更大; (2)当执行插入操作 ...

  2. Java学习笔记八:Java的流程控制语句之循环语句

    Java的流程控制语句之循环语句 一:Java循环语句之while: 生活中,有些时候为了完成任务,需要重复的进行某些动作.如参加 10000 米长跑,需要绕 400 米的赛道反复的跑 25 圈.在 ...

  3. Leecode刷题之旅-C语言/python-104二叉树最大深度

    /* * @lc app=leetcode.cn id=104 lang=c * * [104] 二叉树的最大深度 * * https://leetcode-cn.com/problems/maxim ...

  4. 004---Linux系统设置

    Linux版本相关命令 查看系统版本:cat /etc/redhat-release 查看系统内核版本以及位数:uname -r [root@hostname1 ~]# cat /etc/redhat ...

  5. 记一次MD5妙用

    记一次MD5妙用 最近项目组中在做历史记录的改造工作,主持讨论了多次,但每次讨论完都觉的很完美了,但实际在写这部分逻辑的时候还是会发现一些问题出来,很难受,反反复复的暴露智商是硬伤,人艰不拆,暂先不扯 ...

  6. 【Leetcode】709. To Lower Case

    To Lower Case Description Implement function ToLowerCase() that has a string parameter str, and retu ...

  7. ORB-SLAM 代码笔记(二)

    ORB-SLAM中除了第三方库,基本没有看到使用c++11的新特性(例如别的SLAM框架中常用的智能指针,拷贝控制,泛型算法等,基本没有使用动态内存,栈内存读取速度较快),因此非常适合初学,代码很清晰 ...

  8. leetcode笔记--1 two-sum

    my answer: ​​​​出现的问题:倒数第二行and i !=s这种情况没有考虑进去,以后要思考全面些

  9. static和final的区别(转载)

    Java中static 和final的区别 final定义的变量可以看做一个常量,不能被改变: final定义的方法不能被覆盖: final定义的类不能被继承. final static 就是再加上s ...

  10. 做模态弹框的时候,防止背景滚动方法 移动端 html5

    $(window.document).bind("touchmove", function() { return false; });