树上最值路径 题解

news/2024/10/2 22:21:58

题意简述

给你一棵 \(n\) 个结点的树,编号为 \(1 \sim n\),求有多少路径 \(\operatorname{Path}(u \rightarrow v)\),满足 \(u = \max \{ x \mid x \in \operatorname{Path}(u \rightarrow v) \}\)\(v = \min \{ x \mid x \in \operatorname{Path}(u \rightarrow v) \}\)

\(n \leq 2 \times 10^6\)

题目分析

考虑从小到大的顺序枚举 \(u\),去掉 \(\max\) 的限制,因为 \(u\) 必然是之前的点中的 \(\max\)\(\min\) 从大到小扫同理。在加入 \(u\) 之前,这棵树是若干个联通块。\(u\) 和其相邻的联通块之中的任意一点 \(v\),都能保证 \(u = \max \{ x \mid x \in \operatorname{Path}(u \rightarrow v) \}\)。我们考虑并查集缩点,每次在一棵新的树上,把代表联通块的点和 \(u\) 之间连一条边,并将其在并查集上的父亲设为 \(u\)。这实际上是一个重构树的过程,这个新树有一个很好的性质,那就是当且仅当 \(v\)\(u\) 在新树上的所有后代,使得 \(u = \max \{ x \mid x \in \operatorname{Path}(u \rightarrow v) \}\)。我们将一个奇怪的最值限定,变成了树上祖先后代的限定。我们将这棵树称作 \(\max\) 树。

我们现在有了两棵重构树,分别满足 \(\min\)\(\max\) 的限制。然后就要考虑如何统计答案了,也就是有多少对 \((u, v)\),满足在 \(\max\) 树上,\(u\)\(v\) 的祖先,在 \(\min\) 树上,\(v\)\(u\) 的祖先。我们还是考虑枚举,在 \(\min\) 树上枚举一个 \(u\),查询有多少 \(\min\) 树上 \(u\) 的祖先 \(v\),满足在 \(\max\) 树上 \(v\)\(u\) 的后代。祖先的限制可以用 dfs 栈的思想,用一个数据结构实时维护 dfs 栈中的元素。后者查询后代操作,可以用 dfs 序 + 树状数组区间查询。那么 dfs 栈就是树状数组的单点加操作。

时间复杂度:\(\Theta(n (\alpha(n) + \log n))\)

代码

#include <cstdio>
#include <iostream>
using namespace std;const int N = 2000010;struct Graph {struct node {int to, nxt;} edge[N << 1];int tot, head[N];inline void add(int u, int v) {edge[++tot] = {v, head[u]};head[u] = tot;}inline node & operator [] (int x) {return edge[x];}
} xym, yzh1, yzh2;int n;
long long ans;int fa[N];
int get(int x) {return fa[x] == x ? x : fa[x] = get(fa[x]);
}int tree[N];
inline void modify_add(int p) {for (int i = p; i <= n; i += i & -i)++tree[i];
}
inline void modify_sub(int p) {for (int i = p; i <= n; i += i & -i)--tree[i];
}
inline int query(int p) {int res = 0;for (int i = p; i; i &= i - 1)res += tree[i];return res;
}int L[N], R[N], timer;
void dfs(int now) {L[now] = ++timer;for (int i = yzh1.head[now]; i; i = yzh1[i].nxt)dfs(yzh1[i].to);R[now] = timer;
}void redfs(int now) {ans += query(R[now]) - query(L[now] - 1);modify_add(L[now]);for (int i = yzh2.head[now]; i; i = yzh2[i].nxt)redfs(yzh2[i].to);modify_sub(L[now]);
}signed main() {#ifndef XuYuemingfreopen("charity.in", "r", stdin);freopen("charity.out", "w", stdout);#endifscanf("%d", &n);for (int i = 1, f; i <= n; ++i) {scanf("%d", &f), fa[i] = i;if (f == 0) continue;xym.add(i, f), xym.add(f, i);}for (int u = 2; u <= n; ++u) {for (int i = xym.head[u]; i; i = xym[i].nxt) {int v = xym[i].to;if (v > u) continue;yzh2.add(u, v = get(v));fa[v] = u;}}for (int i = 1; i <= n; ++i) fa[i] = i;for (int u = n - 1; u >= 1; --u) {for (int i = xym.head[u]; i; i = xym[i].nxt) {int v = xym[i].to;if (v < u) continue;yzh1.add(u, v = get(v));fa[v] = u;}}dfs(1), redfs(n), printf("%lld", ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/67219.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

29. GIL全局解释器锁、信号量、线程池进程池

1. GIL全局解释器锁1.1 概念 In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiplenative threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (How…

[操作系统]进程同步

临界区 互斥量 信号量 事件

05-论说文:审题与立意(2)

争论性材料 描述性材料 审题最难的 有个三段论!! 人工智能的作用 有好有坏 技术变革是中项 三段论 、 这怎么写? 经济联考: 蚂蚁 ==》资源 可持续发展 题干明确 ==》给啥说啥 题干不明确,典故、实验、自然现象==》社会、企业管理 见人…

本文来自博客园,作者:雨中秋,转载请注明原文链接:https://www.cnblogs.com/zengzi/p/18445105,不然会AFO

navicat

一、概述 在现代软件开发和数据管理中,数据库的管理与维护至关重要。无论您是一个开发者、数据分析师,还是数据库管理员,使用一款强大的数据库管理工具能大大提高工作效率。Navicat 就是这样一款备受欢迎的数据库管理工具,支持多种数据库系统,如 MySQL、PostgreSQL、SQLit…

记一次虚拟机无法 ping 通百度的解决方法

先运行ip a查看网卡: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 sc…

Apache POI 创建 Excel

数据来自 通义千问🎈依赖包 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version> </dependency> v5.2.2。创建Excel xlsx 格式。简单版 创建一个包含数据的 Excel 文…