Description

给出一棵边带权(\(c\))的节点数量为 \(n\) 的树,初始树上所有节点都是白色。有两种操作:

  • C x,改变节点 \(x\) 的颜色,即白变黑,黑变白。

  • A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为 \(0\))。

\(q\) 次操作,输出所有查询的答案。

Hint

  • \(1\le n, q\le 10^5\)
  • \(0\le |c|\le 10^3\)

Solution

此题使用轻重链剖分真的麻烦

先树剖,然后根据每一个重链,建出一棵线段树(最后建出的是线段树森而非一棵大线段树,动态开点实现)。设 \(root(x)\) 为结点 \(x\) 对应线段树的根。非链顶结点的 \(root\) 无意义。

线段树上的每个结点维护 \(3\) 个字段:

  • \(lx(x)\) 结点 \(x\) 代表的链上一段区间的 左端点(深度小的)可以到达 以链顶为根的子树 中最远的白点的距离。
  • \(rx(x)\) 结点 \(x\) 代表的链上一段区间的 右端点(深度大的)可以到达 以链顶为根的子树 中最远的白点的距离。
  • \(mx(x)\) 结点 \(x\) 代表满足 LCA 在当前结点区间中的所有白点对 中最大的距离。

那么我们可以这样设计我们的 pushup 函数,注意此处的 \(dep\) 带边权。

#define dis(x) dep[pos[x]] // dis(i) 表示区间中位置 i 所对应结点的深度
void pushup(int x, int l, int r) { // x 为线段树上当前结点,对应区间为 [l, r]
lx[x] = max(lx[lc[x]], lx[rc[x]] + dis(mid + 1) - dis(l));
// 可以从左儿子转移而来,也可以从右儿子跨越中间而来。
rx[x] = max(rx[rc[x]], rx[lc[x]] + dis(r) - dis(mid));
// 可以从右儿子转移而来,也可以从左儿子跨越中间而来。
mx[x] = max(max(mx[lc[x]], mx[rc[x]]), lx[rc[x]] + rx[lc[x]] + dis(mid + 1) - dis(mid));
// 可以从儿子结点转移而来,或者计算出跨越中心情况的答案。
// dis 的差值实质上是边权
}
#undef dis

答案即为 \(\max\{mx\}\)。

如何处理叶结点的值?显然不能爆算子树中所有结点的距离,因为深度总和会达到 \(O(n^2)\) 级别。

对于一个 线段树上 的叶子结点 \(x\),其对应的 原树 结点为 \(u\)。对于其 父结点或重儿子,由于在同一条链上 ,无需过多考虑。\(u\) 的所有轻儿子,显然它们一定是其所在链的链顶。

对于其中一个轻儿子 \(v\),易知 \(lx(root(v))\) 子树 \(v\) 中向下延伸的最长合法路径。那么加上当前路径就是 一条“LCA 位于区间 \([dfn(u), dfn(u)]\)”的合法路径。(\(dfn(x)\) 表示原树上结点 \(x\) 的 dfs 序)。

设 \(d_1\) 为一端为 \(u\) 的 最长向下 路径长,\(d_2\) 为 次长向下 路径长。不存在设为 \(-\infty\)。

不难得出,\(lx(x), rx(x)\) 的值就是 \(\max(d_1, 0)\)。\(mx(x)\) 的值可以由最长、次长两条路径拼成。无需考虑路径会不会重合,因为来自不同的子树。由于白点自身可以作为路径的端点,\(mx(x)\) 的值需要分类讨论。

  • 白点:\(mx(x) = \max(d_1, d_1 + d_2, 0)\)。
  • 黑点:\(mx(x) = \max(d_1 + d_2, 0)\)。

对于最大值、次大值的维护,可以使用堆。在叶结点遍历轻儿子时顺便将堆更新。求次大值时只需将堆顶弹出,取值后重新塞回即可。

答案即为 \(\max\limits_{x\in \text{tops}} \{ mx(root(x))\}\),同样可以用一个全局堆维护。

建树操作参考代码:

void build(int& x, int l, int r) {
if (!x) x = ++total; // 动态开点
if (l == r) {
int u = pos[l];
getEdge(u, v) if (v->to != fa[u] && v->to != wson[u])
pt[u].insert(lx[root[v->to]] + dep[v->to] - dep[u]);
// pt 为堆
int d1 = pt[u].top(); // 最大
pt[u].erase(d1);
int d2 = pt[u].top(); // 次大
pt[u].insert(d1); lx[x] = rx[x] = max(d1, 0);
mx[x] = max(d1, max(d1 + d2, 0));
return;
}
build(lc[x], l, mid);
build(rc[x], mid + 1, r);
pushup(x, l, r);
}

考虑修改操作。一个修改可能 会影响到其祖先的答案,于是我们需要一直向上跳。

设当前跳到的位置为 \(x\),上次位置的链顶为 \(y\)。

首先在 链顶父亲 结点的堆中删去 当前链顶的贡献,下一次跳在重新将 更新过的值插入

那么在线段树上修改时,将堆中 \(y\) 方向轻儿子的贡献 重新插入 \(x\) 的堆中,像 build 一样维护即可。

同时别忘了更新全局堆。

下面给出修改的代码:

void update(int x, int l, int r, int u, int v) {
if (l == r) {
if (u != v)
pt[u].insert(lx[root[v]] + dep[v] - dep[u]); int d1 = pt[u].top();
pt[u].erase(d1);
int d2 = pt[u].top();
pt[u].insert(d1); if (color[u]) {
lx[x] = rx[x] = d1;
mx[x] = d1 + d2;
} else {
lx[x] = rx[x] = max(d1, 0);
mx[x] = max(d1, max(d1 + d2, 0));
}
return;
}
if (dfn[u] <= mid) update(lc[x], l, mid, u, v);
else update(rc[x], mid + 1, r, u, v);
pushup(x, l, r);
} void change(int x) {
color[x] ^= 1;
if (color[x] == 0) ++white;
else --white; for (int y = x; x; x = fa[x]) {
int top = wtop[x];
all.erase(mx[root[top]]); if (fa[top]) pt[fa[top]].erase(lx[root[top]] + dep[top] - dep[fa[top]]);
update(root[top], dfn[top], dfn[top] + len[top] - 1, x, y); all.insert(mx[root[top]]);
y = x = top;
}
}

那么算法基本算是完成了。

但实现非常复杂,细节多(上面代码)。

不过实测表现不差,原因是树剖、线段树的 \(\log\) 都跑不满。

时间复杂度 \(O(n\log^2 n)\)。

参考代码:https://vjudge.net/solution/26745510/8T7tgRJPSKwBPUqVL9r2

【SPOJ QTREE4】Query on a tree IV(树链剖分)的更多相关文章

  1. SPOJ QTREE4 - Query on a tree IV 树分治

    题意: 给出一棵边带权的树,初始树上所有节点都是白色. 有两种操作: C x,改变节点x的颜色,即白变黑,黑变白 A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为0). 分析: ...

  2. QTREE3 spoj 2798. Query on a tree again! 树链剖分+线段树

    Query on a tree again! 给出一棵树,树节点的颜色初始时为白色,有两种操作: 0.把节点x的颜色置反(黑变白,白变黑). 1.询问节点1到节点x的路径上第一个黑色节点的编号. 分析 ...

  3. spoj 375 Query on a tree(树链剖分,线段树)

      Query on a tree Time Limit: 851MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Sub ...

  4. SPOJ 375 Query on a tree(树链剖分)(QTREE)

    You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...

  5. SPOJ QTREE - Query on a tree 【树链剖分模板】

    题目链接 引用到的大佬博客 代码来自:http://blog.csdn.net/jinglinxiao/article/details/72940746 具体算法讲解来自:http://blog.si ...

  6. SPOJ 375 Query on a tree(树链剖分)

    https://vjudge.net/problem/SPOJ-QTREE 题意: 给出一棵树,树上的每一条边都有权值,现在有查询和更改操作,如果是查询,则要输出u和v之间的最大权值. 思路: 树链剖 ...

  7. SPOJ QTREE Query on a Tree【树链剖分模板题】

    树链剖分,线段树维护~ #include <cstdio> #include <cstring> #include <iostream> #include < ...

  8. SPOJ 375. Query on a tree (树链剖分)

    Query on a tree Time Limit: 5000ms Memory Limit: 262144KB   This problem will be judged on SPOJ. Ori ...

  9. SPOJ 375 Query on a tree【树链剖分】

    题目大意:给你一棵树,有两个操作1.修改一条边的值,2.询问从x到y路径上边的最大值 思路:如果树退化成一条链的话线段树就很明显了,然后这题就是套了个树连剖分,调了很久终于调出来第一个模板了 #inc ...

  10. SPOJ QTREE6 Query on a tree VI 树链剖分

    题意: 给出一棵含有\(n(1 \leq n \leq 10^5)\)个节点的树,每个顶点只有两种颜色:黑色和白色. 一开始所有的点都是黑色,下面有两种共\(m(1 \leq n \leq 10^5) ...

随机推荐

  1. binary hacks读数笔记(装载)

    1.地址空间 在linux系统中,每个进程拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小是由计算机硬件决定的,具体地说,是由CPU的位数决定的.比如,32位硬件平台决定的虚拟地址空间大小:0--2 ...

  2. jm8.6编解码器概述

    自己在学习h264的路上,欢迎讨论交流. 前段时间研究JM出品的h264编码器,代码实在看不下去,因此换了个角度来研究诸多算法--逆向方式(解码),本系列文章记录一些遇到的东西和思考. 1. JM介绍 ...

  3. explain命令---查看mysql执行计划

    引言: 实际项目开发中,由于我们不知道实际查询的时候数据库里发生了什么事情,数据库软件是怎样扫描表.怎样使用索引的,因此,我们能感知到的就只有 sql语句运行的时间,在数据规模不大时,查询是瞬间的,因 ...

  4. 解决calamari无法获取节点信息的bug

    前言 一直在做calamari的相关的一些打包和安装的工作,都是业余弄的东西,所以并没有仔细的进行功能点的验证测试,正好ceph社区群里面有人问了个问题 calamari上是不是能看到ceph的ver ...

  5. Java编译程序和运行过程详解

    java整个编译以及运行的过程相当繁琐,我就举一个简单的例子说明: 编译原理简单过程:词法分析 --> 语法分析 --> 语义分析和中间代码生成 --> 优化 --> 目标代码 ...

  6. 移动端调试Web页面

    移动端调试Web页面 虽然可以在PC下,通过开发者工具,模拟移动端,但是这样只能模拟页面样式,对于代码的执行情况是无法模拟的,所以在此结合实际调试经验,针对安卓与IOS设备,进行总结. IOS 安卓 ...

  7. 怎么借助CrossOver安装想要的Windows程序

    面对安装双系统时的繁琐步骤,以及虚拟机软件那庞大的体积,CrossOver的出现,让一切都变得简单起来. CrossOver自带的一系列的Windows应用,涵盖游戏软件.办公软件.设计软件等多个种类 ...

  8. appium 启动参数配置

    启动配置参数,可以参照官网: http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities from appiu ...

  9. Requests 库的使用

    Python 的标准库 urllib 提供了大部分 HTTP 功能,但使用起来较繁琐.通常,我们会使用另外一个优秀的第三方库:Requests,它的标语是:Requests: HTTP for Hum ...

  10. yii的pathinfo方式实现

    yii2.0在浏览器中默认查看控制器下的方法是  http://ltbk.cn/index.php?r=login/login 要是在浏览器上输出 http://ltbk.cn/index.php/l ...