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. NET Core中使用MediatR实现命令和中介者模式

    NET Core中使用MediatR实现命令和中介者模式 https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令 ...

  2. description方法

    1.NSLog回顾 众所周知,我们可以用NSLog函数来输出字符串和一些基本数据类 1 int age = 11; 2 NSLog(@"age is %i", age); * 第2 ...

  3. java中的修辞

    1.final修辞 final修辞变量分为两种:基本类型(byte,short,int,long,float,double,char,boolean)也是值类型,则一旦初始化,值不能改变:应用类型(S ...

  4. webpack+vue-cli搭建项目 (vue项目重构三)

    在自己的电脑下载了npm 与 node的情况下 可使用vue-cli快速构建vue项目执行命令如下: # 全局安装 vue-cli$ npm install -g vue-cli# 创建一个基于 &q ...

  5. linux字符界面下root用户无法登录成功

    os: rhel5.6_x86_64 ———————————————————————————————————————————— 故障:图形界面登录正常,其他一切正常,但是切换到字符界面时,输入用户ro ...

  6. 在windows环境中使用varnish

    varnish 的windows 版本下载地址:   http://sourceforge.net/projects/cygvarnish/files/windows-zip-bundle/     ...

  7. PHP 统计数据功能 有感

    统计,就是把基本的数据,整合起来. 用到sql的,有group by 功能,count功能,order by功能等等. sql将收集的数据,进行统计分析. 一般情况下,sql处理后得到的数据,还要通过 ...

  8. 转载:利用UDEV服务解决RAC ASM存储设备名

    利用UDEV服务解决RAC ASM存储设备名 本文转载自Maclean Liu :http://www.askmaclean.com/archives/utilize-udev-resolve-11g ...

  9. platform驱动之probe函数

    驱动注册的probe函数 probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工 ...

  10. JavaScript笔记——DOM的操作

    节点及其类型 在JavaScript中,节点分为三种: 元素节点:HTML标签元素. 属性节点: 元素的属性, 可以直接通过属性的方式来操作. 文本节点: 是元素节点的子节点, 其内容为文本. 在什么 ...