BZOJ 4515

树链剖分 + 李超线段树

要求支持区间插入一条线段,然后查询一个区间内的最小值。可以使用李超线段树解决,因为要维护一个区间内的最小值,所以每一个结点再维护一个$res$表示这个区间内的最小值。因为本题把问题搬到了树上,剖一下就可以了。

我们可以把一个点$x$到根的距离$dis_x$记为一个点在二维平面上的横坐标,这样子可以保证一条重链上的点的距离递增,并且不改变点之间的距离。

考虑到一条树链$(x, y, z)$($z$是$lca(x, y)$)上的数字的覆盖情况是先从$x$向上走到$z$,然后从$z$向下走走到$y$,向上走的时候直线的斜率是$-k$,而向下走的时候直线的斜率是$k$,所以我们把一条树链拆成两条线段插入。

现在记录剩下的我写错的地方:

1、链剖的$dfs2$写错了,一定要注意先向下剖分重儿子。

2、在写线段树的时候要注意要求出来的线段交点的坐标其实是没有离散化过的值,需要拿它和中间的坐标的原来的值进行比较。

3、在求最小的时候注意上层的结点的区间可能包含了我们要求的区间,而这时候上层结点的$res$并不能拿来直接使用(可能会使答案变小),需要根据保存的直线的解析式重新计算。

时间复杂度$O(nlog^3n)$,然后只要相信它常数很小就好了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef double db; const int N = 1e5 + ;
const ll Val0 = 123456789123456789LL; int n, qn, tot = , head[N];
int dfsc = , id[N], pos[N], fa[N], top[N], dep[N], siz[N], son[N];
ll dis[N], kk[N << ], bb[N << ]; struct Edge {
int to, nxt;
ll val;
} e[N << ]; inline void add(int from, int to, ll val) {
e[++tot].to = to;
e[tot].val = val;
e[tot].nxt = head[from];
head[from] = tot;
} template <typename T>
inline void read(T &X) {
X = ; char ch = ; T op = ;
for(; ch > ''|| ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} template <typename T>
inline void chkMin(T &x, T y) {
if(y < x) x = y;
} void dfs1(int x, int fat, int depth, ll nowDis) {
fa[x] = fat, dep[x] = depth, siz[x] = , dis[x] = nowDis;
int maxson = -;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs1(y, x, depth + , nowDis + e[i].val); siz[x] += siz[y];
if(siz[y] > maxson)
maxson = siz[y], son[x] = y;
}
} void dfs2(int x, int topf) {
top[x] = topf, pos[id[x] = ++dfsc] = x;
if(!son[x]) return;
dfs2(son[x], topf);
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
} inline int getLca(int x, int y) {
for(; top[x] != top[y]; ) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] > dep[y] ? y : x;
} /* inline ll getDis(int x, int y) {
ll z = getLca(x, y);
return dis[x] + dis[y] - 2 * dis[z];
} */ namespace SegT {
struct Node {
int id;
ll res;
} s[N << ]; #define lc p << 1
#define rc p << 1 | 1
#define mid ((l + r) >> 1)
#define res(p) s[p].res inline void up(int p) {
// res(p) = Val0;
chkMin(res(p), res(lc)), chkMin(res(p), res(rc));
} void build(int p, int l, int r) {
res(p) = Val0, s[p].id = ;
if(l == r) return; build(lc, l, mid);
build(rc, mid + , r);
} void ins(int p, int l, int r, int x, int y, int nid) {
if(x <= l && y >= r) {
/* if(!s[p].cov) {
s[p].cov = 1;
s[p].id = nid;
chkMin(res(p), min(l1, r1));
return;
} */ ll l1 = kk[nid] * dis[pos[l]] + bb[nid], r1 = kk[nid] * dis[pos[r]] + bb[nid];
ll l2 = kk[s[p].id] * dis[pos[l]] + bb[s[p].id], r2 = kk[s[p].id] * dis[pos[r]] + bb[s[p].id];
if(l1 >= l2 && r1 >= r2) return;
if(l1 <= l2 && r1 <= r2) {
s[p].id = nid;
chkMin(res(p), min(l1, r1));
return;
} db loc = 1.0 * (bb[nid] - bb[s[p].id]) / (kk[s[p].id] - kk[nid]);
db md = (db) dis[pos[mid]];
if(l1 > l2) {
if(loc > md) ins(rc, mid + , r, x, y, nid);
else ins(lc, l, mid, x, y, s[p].id), s[p].id = nid;
} else {
if(loc > md) ins(rc, mid + , r, x, y, s[p].id), s[p].id = nid;
else ins(lc, l, mid, x, y, nid);
} chkMin(res(p), min(l1, r1));
up(p);
return;
} if(x <= mid) ins(lc, l, mid, x, y, nid);
if(y > mid) ins(rc, mid + , r, x, y, nid);
up(p);
} ll query(int p, int l, int r, int x, int y) {
if(x <= l && y >= r) return res(p);
ll res = Val0;
if(s[p].id) {
ll ld = dis[pos[max(l, x)]], rd = dis[pos[min(y, r)]];
chkMin(res, min(ld * kk[s[p].id], rd * kk[s[p].id]) + bb[s[p].id]);
}
if(x <= mid) chkMin(res, query(lc, l, mid, x, y));
if(y > mid) chkMin(res, query(rc, mid + , r, x, y));
return res;
} } using namespace SegT; inline void solve(int x, int y) {
ll res = Val0;
for(; top[x] != top[y]; ) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
chkMin(res, query(, , n, id[top[x]], id[x]));
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
chkMin(res, query(, , n, id[x], id[y]));
printf("%lld\n", res);
} inline void addSeg(int x, int y, int nid) {
for(; top[x] != top[y]; ) {
// if(dep[top[x]] < dep[top[y]]) swap(x, y);
ins(, , n, id[top[x]], id[x], nid);
x = fa[top[x]];
}
// if(dep[x] > dep[y]) swap(x, y);
ins(, , n, id[y], id[x], nid);
} int main() {
read(n), read(qn);
for(int i = ; i < n; i++) {
int x, y; ll v;
read(x), read(y), read(v);
add(x, y, v), add(y, x, v);
} dfs1(, , , 0LL), dfs2(, ); /* for(int i = 1; i <= n; i++)
printf("%lld ", dis[pos[i]]);
printf("\n"); */ build(, , n);
bb[] = Val0, kk[] = 0LL;
for(int op, x, y, cnt = ; qn--; ) {
read(op), read(x), read(y);
if(op == ) {
ll k, b;
read(k), read(b); int z = getLca(x, y);
++cnt;
kk[cnt] = -k, bb[cnt] = b + dis[x] * k;
addSeg(x, z, cnt); ++cnt;
kk[cnt] = k, bb[cnt] = b + (dis[x] - 2LL * dis[z]) * k;
addSeg(y, z, cnt);
} else solve(x, y);
} return ;
}

Luogu 4069 [SDOI2016]游戏的更多相关文章

  1. 【题解】Luogu P4069 [SDOI2016]游戏

    原题传送门 看到这种题,想都不用想,先写一个树链剖分 然后发现修改操作增加的是等差数列,这使我们想到了李超线段树 先进性树剖,然后用李超线段树维护区间最小,这样就做完了(写码很容易出错) 复杂度为\( ...

  2. 4515: [Sdoi2016]游戏

    4515: [Sdoi2016]游戏 链接 分析: 树链剖分 + 超哥线段树.注意细节. 代码: #include<cstdio> #include<algorithm> #i ...

  3. 【BZOJ4515】[Sdoi2016]游戏 树链剖分+线段树

    [BZOJ4515][Sdoi2016]游戏 Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 1234567 ...

  4. [luogu]P1070 道路游戏[DP]

    [luogu]P1070 道路游戏 题目描述小新正在玩一个简单的电脑游戏.游戏中有一条环形马路,马路上有 n 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接.小新以某个机器人工厂为起点,按顺时针 ...

  5. [Luogu P3825] [NOI2017] 游戏 (2-SAT)

    [Luogu P3825] [NOI2017] 游戏 (2-SAT) 题面 题面较长,略 分析 看到这些约束,应该想到这是类似2-SAT的问题.但是x地图很麻烦,因为k-SAT问题在k>2的时候 ...

  6. BZOJ4515: [Sdoi2016]游戏

    Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会 ...

  7. bzoj 4515: [Sdoi2016]游戏

    Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会 ...

  8. [SDOI2016]游戏

    Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会 ...

  9. Luogu P4705 玩游戏

    题目描述 Alice 和 Bob 又在玩游戏. 对于一次游戏,首先 Alice 获得一个长度为 ​ 的序列 ​,Bob 获得一个长度为 ​ 的序列 bb.之后他们各从自己的序列里随机取出一个数,分别设 ...

随机推荐

  1. C#进阶之路(七)反射的应用

    反射在C#中的应用还是很多的,但它对代码的性能有一定影响. 反射的性能: 使用反射来调用类型或者触发方法,或者访问一个字段或者属性时clr 需要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的 ...

  2. 剑指offer-第四章解决面试题思路之总结

  3. ul li 水平居中

    li的float:left方法显然有一个问题,就是无法居中(水平),只能使用padding-left或margin-right的方法方法来固定其居中.但这样可能在宽屏与窄屏的显示不一致.使用这种方法主 ...

  4. 你知道PORT吗?

    在TCP协议中,有端口(PORT)的概念,很多人都不知道端口到底是什么.之前介绍过物理地址,也就是网卡地址,做个不恰当的比喻,物理地址(MAC)地址,相当于身份证(唯一),家庭地址是几幢几单元相当于I ...

  5. How to get the MD5 checksum for a file: md5sum, digest, csum, fciv

    LINUX: md5sum fileName In Linux, the md5sum utility can be used: aemtux1:/ % md5sum binary.file 0c46 ...

  6. java分割函数split的用法(二)

    package com.b; public class Sysetm { public static void main(String[] args) { String a=new String(&q ...

  7. vue-cli+webpack项目 修改项目名称

    使用vue-cli+webpack创建的项目,修改文件名称或者更改文件的位置,运营时会报错,是因为npm项目,在安装依赖(node_nodules)时,会记录当前的文件路径,当修改之后就无法正常启动. ...

  8. 迭代器-迭代对象-dir(a)可以查看该数据类型有多少种方法。range(10)在py3里就是一个迭代器,for循环实际就是迭代器的应用

    迭代器 我们已经知道,可以直接作用于for循环的数据烈性有以下几种: 一类是集合数据类型,如list.tuple.dict.set.str,bytes等: 一类是generator,数据结构,包括生成 ...

  9. git的分布式和集中式

    当然,Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面.

  10. 第三章 深入分析Java Web中的中文编码问题

    3.1 几种常见的编码格式 3.1.1 为什么要编码 一个字节 byte只能表示0~255个符号,要表示更多的字符,需要编码. 3.1.2 如何翻译 ASCII码:有128个,用一个字节的低7位表示. ...