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. Ceph部署mon出现0.0.0.0地址

    前言 最近在群里两次看到出现mon地址不对的问题,都是显示0.0.0.0:0地址,如下所示: [root@lab8106 ceph]# ceph -s cluster 3137d009-e41e-41 ...

  2. C# 简易的串口监视上位机实现

    实现上位机和下位机之间的通信,通常使用的是串口通信,接下来实现一个通过上位机和串口调试助手来完成串口通信测试. 首先创建一个WInfrom窗体应用工程文件,创建过程可参考https://www.cnb ...

  3. Java 开发之 Lombok 必知必会

    转载链接地址:https://juejin.im/post/5cf3edf7e51d454f71439c79 1. 前言 在目前众多编程语言中,Java 语言的表现还是抢眼,不论是企业级服务端开发,还 ...

  4. cProfile分析程序性能

    Python标准库中提供了三种用来分析程序性能的模块,分别是cProfile, profile和hotshot,另外还有一个辅助模块stats.这些模块提供了对Python程序的确定性分析功能,同时也 ...

  5. 关于Boom 3D“夜间模式”的妙用

    Boom 3D的"夜间模式"不仅能够优化音效,减小高分贝声音的突然刺激,让你夜晚的聆听效果更加舒适,还能将音效运用到各种环境中. 下面小编就给大家讲一讲如何更好的利用Boom 3D ...

  6. 如何使用MathType输入贝塔符号?

    在文档中使用公式,常常需要输入各种符号.比如要输入三角函数公式,那么就要输入三角函数中的阿尔法α.贝塔β等符号,那么要怎么打出这类符号呢? MathType作为强大的数学公式编辑器,可以用来编辑各种数 ...

  7. 【译】用 React 和 D3 创建图表

    本文翻译自:https://dzone.com/articles/charts-with-modern-react-and-d3 本文将介绍如何利用 D3JS 和 ReactJS 来创建基础图表. R ...

  8. c语言版单链表

    1 //c语言单链表 2 #include <stdio.h> 3 #include <stdlib.h> 4 typedef struct Node 5 { 6 int da ...

  9. netty&websocket

    1.先判断是不是http 消息,不是返回400,是则remove之前添加的http组件,动态添加websocket组件 添加WebSocket Encoder和WebSocket Decoder之后, ...

  10. mq存储文件

    存储文件 启动broker之后我们可以看到下面这些文件 1.config包含了运行期间一些配置信息,主要包括下列信息. consumerFilter.json:主体消息过滤信息 consumerOff ...