前言

题目链接:洛谷 P4114 Qtree1

前置知识:树链剖分


题意

给定一棵树,有修改边权和查询两点之间边权最大值两种操作,对于每个查询输出结果。

解析

已经在前置博客里提到,树链剖分 可以将树上的任意一条路径划分成不超过 \(O(\log n)\) 条连续的链,保证划分出的每条链上的节点 DFS 序 连续。这大大方便了我们维护点权,但该如何维护边权呢?

这里就要介绍一种重要的思想——化边权为点权。

从 \(1\) 号点开始,遍历一遍整棵树,对于每条边,把边权赋在后遍历到的那个点,也就是 \(dep\) 更大的点上。这样可以保证,在跳祖先的过程中,必然能够不重不漏地计算到每个边权。若把边权赋在 \(dep\) 更大的点上则不然。最后就是简单的 树剖+线段树 维护最大值的过程。

查询操作不用多说,就是树剖的常规操作,不断跳祖先查询最大值即可。

修改操作就比较复杂了,要找到第 \(i\) 条边上 \(dep\) 较大的点(因为边权存在这个点上),然后使用线段树单点修改。

这里要注意,使用 链式前向星 存图会比 vector 邻接表方便更多,因为我们要找到输入的第 \(i\) 条边。

代码

// Problem: P4114 Qtree1
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4114
// Memory Limit: 500 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h>
using namespace std; #define int long long
const int N = 100005;
int n, u, v, w, cnt, tot, a, b;
int head[N], top[N], dep[N], dfn[N], heavy[N], son[N], val[N], f[N];
string s; struct edge
{
int from, to, val, nxt;
}e[N << 1]; inline void add_edge(int u, int v, int w)
{
e[++tot] = {u, v, w, head[u]}, head[u] = tot;
} inline void dfs1(int u, int fa)
{
dep[u] = dep[fa] + 1, f[u] = fa, son[u] = 1;
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v == fa) continue;
dfs1(v, u);
son[u] += son[v];
if(son[heavy[u]] <= son[v]) heavy[u] = v;
}
return;
} inline void dfs2(int u, int tp)
{
top[u] = tp, dfn[u] = ++cnt;
if(!heavy[u]) return;
dfs2(heavy[u], tp);
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if(v == f[u] || v == heavy[u]) continue;
dfs2(v, v);
}
return;
} struct node
{
int l, r, maxi;
}tree[N << 2]; inline void push_up(int x)
{
tree[x].maxi = max(tree[x << 1].maxi, tree[x << 1 | 1].maxi);
} inline void build(int l, int r, int x)
{
tree[x] = {l, r, 0};
if(l == r) return (void) (tree[x].maxi = val[l]);
int mid = l + r >> 1;
build(l, mid, x << 1);
build(mid + 1, r, x << 1 | 1);
push_up(x);
return;
} inline void update(int pos, int k, int x)
{
if(tree[x].l == tree[x].r && tree[x].r == pos)
return (void) (tree[x].maxi = k);
int mid = tree[x].l + tree[x].r >> 1;
if(pos <= mid) update(pos, k, x << 1);
if(pos > mid) update(pos, k, x << 1 | 1);
push_up(x);
return;
} inline int query(int l, int r, int x)
{
if(l <= tree[x].l && tree[x].r <= r) return tree[x].maxi;
int mid = tree[x].l + tree[x].r >> 1, ans = 0;
if(l <= mid) ans = max(ans, query(l, r, x << 1));
if(r > mid) ans = max(ans, query(l, r, x << 1 | 1));
return ans;
} inline int ask(int x, int y)
{
int ans = 0;
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x, y);
ans = max(ans, query(dfn[top[x]], dfn[x], 1));
x = f[top[x]];
}
if(dfn[x] > dfn[y]) swap(x, y);
return max(ans, query(dfn[x] + 1, dfn[y], 1));
} signed main()
{
ios :: sync_with_stdio(false);
cin >> n;
for(int i = 1; i < n; i++)
{
cin >> u >> v >> w;
add_edge(u, v, w);
add_edge(v, u, w);
}
dfs1(1, 0);
dfs2(1, 1);
for(int i = 1; i <= tot; i += 2)
{
int u = e[i].from, v = e[i].to;
if(dep[u] < dep[v]) swap(u, v);
val[dfn[u]] = e[i].val;
}
build(1, n, 1);
while(cin >> s)
{
if(s == "DONE") break;
cin >> a >> b;
if(s == "CHANGE")
{
a = a * 2 - 1;
int u = e[a].from, v = e[a].to;
if(dep[u] < dep[v]) swap(u, v);
update(dfn[u], b, 1);
}
else cout << (a == b ? 0 : ask(a, b)) << '\n';
}
return 0;
}

树链剖分 | 洛谷 P4114 Qtree1的更多相关文章

  1. AC日记——【模板】树链剖分 洛谷 P3384

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  2. 洛谷 P4114 Qtree1 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例: 输出样例: 说明 说明 思路 Change Query AC代码 总结 题面 题目链接 P4114 Qt ...

  3. 洛谷P4114 Qtree1

    题目描述 给定一棵\(n\)个节点的树,有两个操作: \(CHANGE\) \(i\) \(t_i\) 把第\(i\)条边的边权变成\(t_i\) \(QUERY\) \(a\) \(b\) 输出从\ ...

  4. 洛谷P4114 Qtree1(树链剖分+线段树)

    传送门 LCT秒天秒地用什么树剖 这题可以算是树剖的比较裸的题目了 把每一条边的权值下放到他两边的点中深度较深的那个 然后直接用树剖+线段树带进去乱搞就可以了 //minamoto #include& ...

  5. 洛谷 - P4114 - Qtree1 - 重链剖分

    https://www.luogu.org/problem/P4114 维护边权的话,用深度大的点表示这条边(可以遍历一边边询问两端深度,这样不需要修改dfs1,也可以在dfs1的时候向下走的同时把边 ...

  6. 洛谷 P4114 Qtree1

    Qtree系列都跟树有着莫大的联系,这道题当然也不例外 我是题面 读完题,我们大概就知道了,这道题非常简单,可以说是模板题.树剖+线段树轻松解决 直接看代码吧 #include<algorith ...

  7. 洛谷 P3384 【模板】树链剖分

    树链剖分 将一棵树的每个节点到它所有子节点中子树和(所包含的点的个数)最大的那个子节点的这条边标记为"重边". 将其他的边标记为"轻边". 若果一个非根节点的子 ...

  8. 树链剖分详解(洛谷模板 P3384)

    洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 emm还有必备的 链式前向星.线段树 也要先学了. 如果这三个知识点没掌握好的话,树链剖 ...

  9. ⌈洛谷1505⌋⌈BZOJ2157⌋⌈国家集训队⌋旅游【树链剖分】

    题目链接 [洛谷] [BZOJ] 题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T ...

  10. BZOJ2243 洛谷2486 [SDOI2011]染色 树链剖分

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2243 题目传送门 - 洛谷2486 题意概括 一棵树,共n个节点. 让你支持以下两种操作,共m次操 ...

随机推荐

  1. 2021-05-22:假设所有字符都是小写字母, 大字符串是str,arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次。使用arr中的单词有多少种拼接str的方式。 返回方法数。

    2021-05-22:假设所有字符都是小写字母, 大字符串是str,arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次.使用arr中的单词有多少种拼接str的方式. 返回方法数. 福大大 ...

  2. ESLint: More than 1 blank line not allowed. (no-multiple-empty-lines)

    ESLint: More than 1 blank line not allowed. (no-multiple-empty-lines)

  3. 聊聊Seata分布式解决方案AT模式的实现原理

    什么是Seata分布式事务解决方案 Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.为用户提供了AT.TCC.SAGA和XA事务模式,为用户打造一站式的分布式解决 ...

  4. Python asyncio之协程学习总结

    实践环境 Python 3.6.2 什么是协程 协程(Coroutine)一种电脑程序组件,该程序组件通过允许暂停和恢复任务,为非抢占式多任务生成子程序.协程也可以简单理解为协作的程序,通过协同多任务 ...

  5. 自学FHQ-treap的草稿

    更新:能过模板题(和加强版)的代码: 普通平衡树: (请自行实现读入和输出函数) 点击查看代码 #include <iostream> #include <random> #i ...

  6. 基于ggplot2的解剖图和组织模块可视化

    摘要 将数据显示到解剖结构上,是一种可以快速观察组织相关信息的便捷技术.然而,绘制组织是一项复杂的任务(a complex task),需要解剖学和艺术方面的专业知识.虽然已经存在可用于在解剖图上显示 ...

  7. C++面试八股文:了解auto关键字吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第15面: 面试官:了解auto关键字吗? 二师兄:嗯,了解一些(我很熟悉). 面试官:说一说auto的用法吧? 二师兄:auto主要是为了编译器进行 ...

  8. 水果识别系统python

    介绍 水果识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法.并通过对5种垃圾数据集进行训练,最后得到一个识别精度较高的模型.并基于Django框架 ...

  9. sql server注入rce实践

    背景:在漏洞挖掘中,合理的利用sql注入,可以把注入转换成rce,使一个高危漏洞变成严重漏洞.在红蓝对抗中,利用注入rce,实现内网横向移动.笔者基于漏洞挖掘和红蓝对抗上遇到的sql server注入 ...

  10. 基于GPT搭建私有知识库聊天机器人(一)实现原理

    1.成品演示 支持微信聊天 支持网页聊天 支持微信语音对话 支持私有知识文件训练,并针对文件提问 步骤1:准备本地文件a.txt,支持pdf.txt.markdown.ppt等 步骤2:上传a.txt ...