前言

题目链接:洛谷 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. Apache Arrow DataFusion原理与架构

    本篇主要介绍了一种使用Rust语言编写的查询引擎--DataFusion,其使用了基于Arrow格式的内存模型,结合Rust语言本身的优势,达成了非常优秀的性能指标 DataFusion是一个查询引擎 ...

  2. DataGridViewImageColumn 图片照片

    Private Sub BT_PHOTOADDRESS_Click(sender As Object, e As EventArgs) Handles BT_PHOTOADDRESS.Click Di ...

  3. 【.NET】C#/.NET新建项目sln,增加src和test文件夹问题和解决方案

    ​ 问题介绍 经常逛github找优秀的.NET项目看,看到github上的项目的层级有src test,sln放在外层.如下图: 发现自己再Visaul Studio新建的项目即使添加了src和te ...

  4. ICLR 2018-A Simple Neural Attentive Meta-Learner

    Key 时序卷积+注意力机制(前者从过去的经验中收集信息,而后者则精确定位具体的信息.) 解决的主要问题 手工设计的限制:最近的许多元学习方法都是大量手工设计的,要么使用专门用于特定应用程序的架构,要 ...

  5. 一文读懂面试官都在问的Log4J2漏洞

    CVE-2021-44228 漏洞简介 Apache Log4j2是一个基于Java的日志记录工具,当前被广泛应用于业务系统开发,开发者可以利用该工具将程序的输入输出信息进行日志记录. 2021年11 ...

  6. 【jmeter】测试socket接口的简单应用

    一.场景 有一天开发问我,有没有什么工具可以测试socket,tcp,当时有点懵,这种需求还是少见 二.方法 使用Jmeter可以进行相关的测试 三.创建服务端环境 使用python搞个socket服 ...

  7. Galaxy Release_20.09 发布,新增多个数据上传组件

    Galaxy Project(https://galaxyproject.org/)是在云计算背景下诞生的一个生物信息学可视化分析开源项目. 该项目由美国国家科学基金会(NSF).美国国家人类基因组研 ...

  8. Simple CTF

    来自tryhackme的Simple CTF IP:10.10.27.234 信息收集 端口扫描 nmap -sV -T4 10.10.27.234 可以看到三个端口 21/tcp 打开 ftp vs ...

  9. 基于 prefetch 的 H5 离线包方案

    前言 对于电商APP来讲,使用H5技术开发的页面占比很高.由于H5加载速度非常依赖网络环境,所以为了提高用户体验,针对H5加载速度的优化非常重要.离线包是最常用的优化技术,通过提前下载H5渲染需要的H ...

  10. 微信小程序如何使用原生Websocket与Asp.Net Core SignalR 通信

    背景 如题,这可能算是.net 做小程序的服务端时,绕不开的一个问题,老生常谈了.同样的问题,我记得我2018/19年的一个项目的解决方案是: 修改官方的SignalR.js的客户端:把里面用到浏览器 ...