能够解决的题目类型

这个 Trick 能解决的题目形如:

  • 给定 \(n\) 个节点的有根无边权有点权树。
  • 有 \(m\) 个询问,每个询问形如点 \(x\) 的子树内与 \(x\) 深度差不超过 \(k\) 的点的极值/排名/和。
  • \(O(n\,log\,n)\) 可过。

优缺点

优点:可以强制在线,代码简单。

缺点:可能被卡常(概率非常小)。

思路

首先,深度差不超过 \(k\) 这个限制很难办,我们无法用一维的编号把这些点串起来,而且没有什么差分方法。

那么——二维,启动!

我们把一个数点 \(x\) 变成二维平面上的点:\((depp_x\,,dfn_x)\)。

那么对于一个点 \(p\) 的询问就变成了横坐标在 \([depp_p\,,depp_p+k]\) 中,纵坐标在 \([dfn_p\,,dfn_p+sz_p-1]\) 的点的极值/排名/和。

这个显然可以用树套树,但是 \(O(n\,log^2\,n)\),有没有更优的方法呢?

我们考虑主席树,但是主席树必须满足可减性,而最值没有。

但是,当一个点的横坐标在属于 \([1,depp_p-1]\) 时,他的纵坐标一定不属于 \([depp_p\,,dep_p+k]\),也就是说我们并不需要将两颗线段树相减,那么询问也就不用讲满足可减性。

所以我们就可以愉快地切题了。

算法流程

  1. 用一个 dfs 求出每个节点的 \(dfn,sz,depp\)。
  2. 把节点按照深度排序,一个一个加入主席树,并记录对于每个深度的最后一课主席树的根的下标 \(idx\)。

例题代码

本 Trick 的代码基本每道题没什么变化,直接套用就行了

CF893F Subtree Minimum Query

#include <bits/stdc++.h>
using namespace std; #define midd ((node[u].l + node[u].r) >> 1)
constexpr int maxn = 100010; int n, r, m, v[maxn], a, b, dfn[maxn], nowdfn, depp[maxn], s[maxn], idx[maxn], sz[maxn], lans, maxdep = 0;
vector <int> G[maxn]; struct nodee {
int v, l, r, ls, rs;
} ;
struct tr {
nodee node[maxn << 5];
int root[maxn], cnt;
void build(int &u, int l, int r) {
u = ++cnt;
node[u] = {1000000000, l, r, 0, 0};
if (l == r) return ;
build(node[u].ls, l, midd);
build(node[u].rs, midd + 1, r);
return ;
}
void update(int &u, int pree, int x, int k) {
u = ++cnt;
node[u] = node[pree];
node[u].v = min(node[u].v, k);
if (node[u].l == node[u].r) return ;
if (x <= midd) update(node[u].ls, node[pree].ls, x, k);
else update(node[u].rs, node[pree].rs, x, k);
return ;
}
int query(int u, int l, int r) {
if (node[u].l >= l && node[u].r <= r) return node[u].v;
int ress = 1000000000;
if (l <= midd) ress = min(query(node[u].ls, l, r), ress);
if (r > midd) ress = min(query(node[u].rs, l, r), ress);
return ress;
}
} t; void dfs(int u, int fa) {
sz[u] = 1;
dfn[u] = ++nowdfn;
for (int now : G[u]) {
if (now == fa) continue;
depp[now] = depp[u] + 1;
maxdep = max(maxdep, depp[now]);
dfs(now, u);
sz[u] += sz[now];
}
return ;
} signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); cin >> n >> r;
depp[r] = 1;
for (int i = 1; i <= n; i++) {
s[i] = i;
cin >> v[i];
}
for (int i = 1; i < n; i++) {
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
dfs(r, 0);
sort(s + 1, s + 1 + n, [](int a, int b){return depp[a] < depp[b];});
// 以上为第一部分 dfs
t.build(t.root[0], 1, n);
idx[0] = t.root[0];
for (int i = 1; i <= n; i++) {
t.update(t.root[i], t.root[i - 1], dfn[s[i]], v[s[i]]);
if (depp[s[i]] != depp[s[i + 1]]) idx[depp[s[i]]] = i;
}
// 以上为第二部分 主席树预处理
cin >> m;
while (m--) {
cin >> a >> b;
a = (a + lans) % n + 1;
b = (b + lans) % n;
lans = t.query(t.root[idx[min(maxdep, depp[a] + b)]], dfn[a], dfn[a] + sz[a] - 1);
cout << lans << '\n';
} return 0;
}

数据结构 Trick 之:子树 k 距离内问题的更多相关文章

  1. 【LeetCode】358.K 距离间隔重排字符串

    358.K 距离间隔重排字符串 知识点:哈希表:贪心:堆:队列 题目描述 给你一个非空的字符串 s 和一个整数 k,你要将这个字符串中的字母进行重新排列,使得重排后的字符串中相同字母的位置间隔距离至少 ...

  2. OpenJudge数据结构与算法-计算点的距离并排序

    /*================================================================== 距离排序 总时间限制: 1000ms 内存限制: 65536k ...

  3. 排序算法 以及HKU的一些数据结构 相关题目 以及 K叉树,二叉树 排列

    冒泡排序.选择排序.快速排序.插入排序.希尔排序.归并排序.基数排序以及堆排序,桶排序 https://www.cnblogs.com/Glory-D/p/7884525.html https://b ...

  4. [Leetcode 787]中转K站内最便宜机票

    题目 n个城市,想求从src到dist的最廉价机票 有中转站数K的限制,即如果k=5,中转10次机票1000,中转5次机票2000,最后返回2000 There are n cities connec ...

  5. Laravel 框架根据经纬度计算在一定距离内的数据

    $model = DB::table('table_name'); public static function scope_distance($model, $from_latitude, $fro ...

  6. HDU 4347 - The Closest M Points - [KDTree模板题]

    本文参考: https://www.cnblogs.com/GerynOhenz/p/8727415.html kuangbin的ACM模板(新) 题目链接:http://acm.hdu.edu.cn ...

  7. LA3902 Network

    给出一棵树,对于每一个叶子节点要使得在它的k距离内至少一个节点被打了标记,(叶节点不能打标记,非叶结点也不必满足这个条件),现在已经有一个节点s被打了标记,问至少还要打几个标记(这表达能力也是捉急.. ...

  8. Leetcode——863.二叉树中所有距离为 K 的结点

    给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 K . 返回到目标结点 target 距离为 K 的所有结点的值的列表. 答案可以以任何顺序返回. 示例 1: 输 ...

  9. php mysql 计算经纬之间距离 范围内筛选

    <?php /** * 根据经纬度和半径计算出范围 * @param string $lat 纬度 * @param String $lng 经度 * @param float $radius ...

  10. 常用查找数据结构及算法(Python实现)

    目录 一.基本概念 二.无序表查找 三.有序表查找 3.1 二分查找(Binary Search) 3.2 插值查找 3.3 斐波那契查找 四.线性索引查找 4.1 稠密索引 4.2 分块索引 4.3 ...

随机推荐

  1. 一文彻底弄懂JUC工具包的Semaphore

    Semaphore 是 Java 并发包 (java.util.concurrent) 中的重要工具,主要用于控制多线程对共享资源的并发访问量.它可以设置"许可证"(permit) ...

  2. Cursor使用

    Cursor是一款AI 代码编辑器,官网地址为https://www.cursor.com/,直接在官网下载安装即可,基于VS Code二次开发而来,之所以没有采用插件方式,在官方网站上给出的答案是某 ...

  3. Abp源码分析之虚拟文件系统Volo.Abp.VirtualFileSystem

    前言 Volo.Abp.VirtualFileSystem 是ABP(ASP.NET Boilerplate)框架中的一个重要组件,它提供了一种抽象文件系统的方式,使得应用程序可以轻松地访问和管理文件 ...

  4. CAD Build 隐私说明

    该App不会收集任何用户隐私数据.

  5. thinkpad x250装manjaro linux,解决指纹和远程桌面问题

    前言 家里有个thinkpad x250闲置,平时主要用windows,大概隔一年半年装一次linux看看发展程度. 自己平时用的服务器一般装centos和debian,偶尔是ubuntu. 桌面li ...

  6. 接口自动化AES对称加密为什么密钥key是16位的?

    对称加密AES,加密和解密的密钥是同一个 AES是一个分组加密算法,AES有三种密钥长度(128.192.256)比特,常用的是128比特,也就是16位 AES常用的加密模式有:ECB,ECB是将明文 ...

  7. nginx防盗链接的使用

    以 local.hyperf.com为例 nginx配置文件如下 # 至少需要一个 Hyperf 节点,多个配置多行 upstream hyperf { # Hyperf HTTP Server 的 ...

  8. JAVA并发编程学习笔记之synchronized

    监视器 java中同步是通过监视器模型来实现的,JAVA中的监视器实际是一个代码块,这段代码块同一时刻只允许被一个线程执行.线程要想执行这段代码块的唯一方式是获得监视器. 监视器有两种同步方式:互斥与 ...

  9. 数据万象 | AIGC 存储内容安全解决方案

    AIGC(人工智能生产内容) 已经成为与PGC(专业生产内容).UGC(用户生产内容)并驾齐驱的内容生产方式.由于 AI 的特性,AIGC在创意.个性化.生产效率等方面具有独特的优势,这些优势可以使得 ...

  10. Flutter查漏补缺2

    Flutter的理念架构 Flutter架构分为三层 参考官方文档 Framework层(dart) flutter engine层(C/C++) embeder层(platform-specific ...