首页 > 其他分享 >树链剖分 | 洛谷 P4114 Qtree1

树链剖分 | 洛谷 P4114 Qtree1

时间:2023-08-20 19:15:07浏览次数:33  
标签:dep P4114 洛谷 Qtree1 边权 树链 int dfn

前言

题目链接:洛谷 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;
}

标签:dep,P4114,洛谷,Qtree1,边权,树链,int,dfn
From: https://www.cnblogs.com/Heartquakes/p/17644374.html

相关文章

  • 普及模拟2 +【LGR-155-Div.3】洛谷基础赛 #3 &「NnOI」Round 2
    普及模拟2\(T1\)地址\(0pts\)简化题意:判断一个\(IP\)地址是否合法(数据保证字符串中存在且仅存在4个被字符分开的整数)#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#definesortstable_sort#defineendl'\n'chars[100];intmain(){ freope......
  • 【洛谷 1157】组合的输出
    #组合的输出##题目描述排列与组合是常用的数学方法,其中组合就是从$n$个元素中抽出$r$个元素(不分顺序且$r\len$),我们可以简单地将$n$个元素理解为自然数$1,2,\dots,n$,从中任取$r$个数。现要求你输出所有组合。例如$n=5,r=3$,所有组合为:$123,124,125,134,135,145,......
  • 洛谷P5410 【模板】扩展 KMP(Z 函数)题解
    题目链接P5410【模板】扩展KMP(Z函数)-洛谷|计算机科学教育新生态(luogu.com.cn) 分析先考虑z数组设nx[i]为字符串b与b以b[i]开头的后缀最长公共前缀设i为当前需要求的位置当前i+nx[i]-1的最大值所对应的i为q p为i对应的位置当i大于q+nx[q......
  • 洛谷_[P4084]Barn Painting G题解
    题目链接这题我们可以定义一个二维的dp数组,在dp[i][j]中:i表示对于节点i,j有1,2,3三种状态,表示当点i选择被染成颜色j时,以i为根的这颗子树有多少种染色方法。那么根据乘法原理,节点i的方案数肯定等于i的每个儿子的方案数量之积。这道题整个挺简单的,剩下细节......
  • 洛谷 P6620 [省选联考 2020 A 卷] 组合数问题
    前置知识二项式定理\[(x+y)^n=\sum_{i=0}^n\binomnix^iy^{n-i}\]组合恒等式\[k\times\binomnk=n\times\binom{n-1}{k-1}\]题解先不管取模的事情。考虑把\(f(k)\)中次数相同的项拿出来,则原式可化为:\[Ans=a_0\sum_{k=0}^nx^k\times\binomnk......
  • 删数问题 洛谷p1323
    决定做一系列贪心,贪心真的,最早学的算法,到现在还有时候不太敢贪,还贪不来,一直挺逃避贪心问题的。。 删除前的数字可以先用优先队列对所有数字进行预处理,数据范围是3e4,也不是很大,直接全部处理了吧。constintN=1e5+10,inf=0x3f3f3f3f3f3f3f3f,MAX=3e4+10;inta[N]......
  • 【题解】洛谷 P9532 [YsOI2023] 前缀和
    原题链接【LGR-151-Div.2】洛谷8月月赛II&YsOI2023T1解题思路设有一序列a,其中a1 =a2,第k(≥3) 项为前k-1项的前缀和。可以发现前q项分别为第一项的20 倍,20 倍,21 倍,22 倍,23 倍…2q-3 倍,2q-2 倍。扩展到整个序列中,可得第i( ≥ 3)项一定为2i-2a1。......
  • 洛谷P9533 区间翻转区间异或和 题解
    原题:洛谷P9533一道性质题不难发现,区间翻转操作是没有用的(虽然比赛的时候想了好久www)首先,区间翻转要想对答案有贡献,一定是下边这种情况:三个连续的区间:\(A~|~B~|~C\)满足:\(B\oplusC=0,A\oplusC=0\)。将\(B∪C\)这个灵异区间进行翻转,使\(A\)和\(C\)合并到一起,会增加一......
  • 2023年多校联训NOIP层测试7+【LGR-149-Div.3】洛谷基础赛 #2 & qw Round -1
    2023年多校联训NOIP层测试7,集训欢乐赛,绝对欢乐,童叟无欺赛时在回家的路上+睡觉,所以没打。\(T1\)近似ybtOJ2049:【例5.19】字符串判等本题少了对空格的判断,水题。PS:题面和题解中都写了文件输入输出,测评时没有文件输入输出是几个意思,艹。#include<bits/stdc++.h>usingname......
  • 【LGR-149-Div.3】洛谷基础赛 #2 & qw Round -1
    T1签到。T2送分题。T3大模拟,但是TLE两个点。#include<bits/stdc++.h>#definelllonglong#defineintlonglong#definereregisterusingnamespacestd;constintN=1e5+5,INF=0x3f3f3f3f;intn;queue<string>Q;map<string,int>zt;//0不在;1排队;2游玩;......