P4197 Peaks(主席树+Kruskal重构树+倍增)

2023-11-05

传送门

前缀知识

Kruskal重构树

题面

在这里插入图片描述
在这里插入图片描述

思路

看到困难值小于等于x就应该想到用Kruskal重构树了;

首先我们构建一颗Kruskal重构树,然后询问是问我们从某个点 u u u出发,不超过困难值 h h h,那么这个过程我们可以用倍增来解决;

接着看到第 k k k大,那么维护第 k k k大可以用很多数据结构;

假设当前不超过困难值 h h h这个点为 r t rt rt,因为每个 r t rt rt都不同,维护的区间也不同,因此是一个动态区间第k大,因此我们考虑主席树来解决;

我们用一个数组 r a n g e ( i ) ( 2 ) range(i)(2) range(i)(2),其中 r a n g e ( i ) ( 0 ) range(i)(0) range(i)(0)表示子树 i i i的左边界, r a n g e ( i ) ( 1 ) range(i)(1) range(i)(1)表示右边界,其中是左开右闭的,方便相减;

然后在dfs的过程中就可以就可以维护出来了;

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10,M = 5e5 + 10;

int n,m,q,a[N];

vector<int> ve;

struct Node{
	int p1,p2,val;
	bool operator<(const Node o)const{
		return val < o.val;
	}
}K[M<<1];
struct Edge{
	int to,next,val;
}e[M];
int p[N],head[N],tot,pval[N];
int find(int x){
	if(x == p[x]) return x;
	return p[x] = find(p[x]);
}
int get(int x){
	return lower_bound(ve.begin(),ve.end(),x) - ve.begin() + 1;
}
void add(int u,int v){
	e[++tot].to = v;
	e[tot].next = head[u];
	head[u] = tot;
}
struct Tree{
	int lc,rc;
	int sum;//为了找第k大
}tr[N<<5];
int cnt;//主席树索引
void push_up(int p){
	tr[p].sum = tr[tr[p].lc].sum + tr[tr[p].rc].sum;
}
int root[N];//版本数组
int build(int l,int r){
	int p = ++cnt;
	if(l == r){
		return p;
	}
	int mid = (l+r) >> 1;
	build(l,mid);
	build(mid+1,r);
	return p;
}
int update(int p,int l,int r,int tar){
	int q = ++cnt;
	tr[q] = tr[p];
	if(l == r){
		++tr[q].sum;
		return q;
	}
	int mid = (l + r) >> 1;
	if(tar <= mid) tr[q].lc = update(tr[p].lc,l,mid,tar);
	else tr[q].rc = update(tr[p].rc,mid+1,r,tar);
	push_up(q);
	return q;
}
int query(int p,int q,int l,int r,int k){//返回的是值
	if(l == r){
		return ve[l-1];
	}
	int mid = (l+r) >> 1;
	int dif = tr[tr[q].rc].sum - tr[tr[p].rc].sum;
	//第k大,先看右树
	if(k <= dif){
		return query(tr[p].rc,tr[q].rc,mid+1,r,k);
	}
	else return query(tr[p].lc,tr[q].lc,l,mid,k-dif);
}
//range是左开右闭的,方便前缀和(主席树操作)
int range[N<<1][2];//range(i)(0)表示子树i的左边界,(1)表示右边界
int fa[N][25];//倍增数组
int pos;//我们定义的位置
void dfs(int u){
	for(int i=1;i<=20;++i)
		fa[u][i] = fa[fa[u][i-1]][i-1];
	range[u][0] = pos;
	if(head[u] == 0){//是叶子结点
		//在主席树上插入
		int x = get(a[u]);
		++pos;
		range[u][1] = pos;//左开右闭
		root[pos] = update(root[pos-1],1,ve.size(),x);
		return;
	}
	for(int i=head[u];i;i=e[i].next){
		int to = e[i].to;
		dfs(to);
	}
	range[u][1] = pos;
}
void EX_Kru(){
	for(int i=1;i<=2*n-1;++i) p[i] = i;
	int now = n;
	for(int i=1;i<=m;++i){
		int u,v,w;
		cin >> u >> v >> w;
		K[i] = {u,v,w};
	}
	sort(K+1,K+1+m);
	for(int i=1;i<=m;++i){
		int u = K[i].p1,v = K[i].p2,w = K[i].val;
		int fu = find(u),fv = find(v);
		if(fu != fv){
			//merge
			p[fu] = p[fv] = ++now;
			pval[now] = w;
			add(now,fu),add(now,fv);
			fa[fu][0] = fa[fv][0] = now;
		}
	}
	//dfs前先建树
	root[0] = build(1,ve.size());
	//当前的树根是now
	dfs(now);
}
void solve(){
	cin >> n >> m >> q;
	for(int i=1;i<=n;++i)
		cin >> a[i],ve.push_back(a[i]);
	sort(ve.begin(),ve.end());
	ve.erase(unique(ve.begin(),ve.end()),ve.end());
	EX_Kru();
	while(q--){
		int u,h,k;//从点u出发,高度不超过h,第k大
		cin >> u >> h >> k;
		//倍增
		for(int i=20;i>=0;--i){
			int anc = fa[u][i];
			if(anc && pval[anc] <= h) u = anc;
		}
		//u现在是某个子树的根
		int L = range[u][0],R = range[u][1];//左开右闭
		if(tr[root[R]].sum - tr[root[L]].sum < k){
			//不足k个数
			cout << -1 << '\n';
		}
		else{
			cout << query(root[L],root[R],1,ve.size(),k) << '\n';
		}
	}
}

signed main(){
	std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	solve();
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

P4197 Peaks(主席树+Kruskal重构树+倍增) 的相关文章

随机推荐

  • Java中的直接赋值、浅拷贝和深拷贝

    将一个对象的引用复制给另外一个对象 一共有三种方式 第一种方式是直接赋值 第二种方式是浅拷贝 第三种是深拷贝 所以大家知道了哈 这三种概念实际上都是为了拷贝对象啊 直接赋值 好 下面我们先看第一种方式 直接赋值 在Java中 A a1 a2
  • 华为OD机试 - 数组分组(C++ & Java & JS & Python)

    描述 输入int型数组 询问该数组能否分成两组 使得两组中各元素加起来的和相等 并且 所有5的倍数必须在其中一个组中 所有3的倍数在另一个组中 不包括5的倍数 不是5的倍数也不是3的倍数能放在任意一组 可以将数组分为空数组 能满足以上条件
  • pandas.DataFrame.notnull返回非空值

    gt gt gt import pandas as pd gt gt gt df pd read csv test csv gt gt gt print df name id 0 ZhangSan 1 0 1 LiSi 2 0 2 Wang
  • 关于UML时序图与类图的学习(程序开发)

    时序图 时序图可以帮助我们更加清晰地看到一个系统之中各个对象的运行情况 有助于我们 梳理业务代码的结构 梳理一些框架的源码理清启动流程以及各个层次之间的关系 在绘制时序图的时候 我们需要接触七种元素 角色 Actor 对象 Object 生
  • 七. OpenFeign 服务接口调用

    目录 一 基础介绍 二 使用 OpenFeign 调用服务接口的实现步骤 服务提供方 服务消费方 OpenFeign 调用服务超时问题 OpenFeign 日志打印功能 服务消费方 yml 中配置 Feign 调用超时与 Feign 日志
  • 智源论坛:人工智能与国际知识产权

    人工智能作为新一轮科技革命的核心技术 已经成为推动科技创新和经济发展的关键因素 在政策和市场的双重推动下 中国人工智能产业已进入高速发展阶段 技术红利快速释放 创新成果不断涌现 应用领域日益拓展 其中 厘清人工智能与知识产权保护的关系 是确
  • 即时AI设计领跑新视界

    Part1前言 随着人工智能技术的不断发展 越来越多的设计师开始关注AIGC Artificial Intelligence Generated Content 并开始思考AIGC对设计师的影响以及如何运用AIGC即时为设计服务 作为全球首
  • 最新RemObjects,您值得拥有

    最近想学习一下分布式等技术 在网上找了一些资料 为了方便俺记了下来 以下转载自 http blog csdn net chinaeband archive 2009 06 19 4282506 aspx http blog csdn net
  • 中南林注册教育邮箱加获取JetBrains个人许可证,续订许可证

    文章目录 1 注册中南林教育邮箱 1 1 注册 1 2 登录 1 3 speech balloon 有话说 2 注册JetBrains个人账号 2 1 进入登录 注册 2 2 查看邮箱信息 2 3 填写基本信息并提交 2 4 注册成功 3
  • 软件测试的艺术

    软件测试 软件测试 是一个过程或一系列过程 用来确认计算机代码完成了其应该完成的功能不执行其不该有的操作 软件应当是可预测且稳定的 不会给用户带来意外惊奇 更准确的说 测试是为发现错误而执行程序的过程 软件测试的重要原则 编号 原则 0 软
  • Java项目---图片服务器

    图片服务器 gt 服务器 图床 核心功能 上传图片 展示图片等 比如 编写博客时我们会插入图片 本质上是往文章中放了一个链接 URL 这个URL资源在另外一个服务器上 核心知识点 1 简单的Web服务器设计开发能力 Servlet Web服
  • Linux编程(获取系统时间)

    include
  • 事务中间件 CICS 原理及应用开发

    https www ibm com developerworks mydeveloperworks blogs cicschina entry cics transaction gateway69 lang en 事务中间件 CICS 原理
  • 转行IT为什么必须学Python?Python的职业发展是什么?

    Python这个词估计听烂了 那么为什么那么多小伙伴都在学Python呢 Python到底有啥魔力 学了Python都能干啥 为什么有必要学python 1 为什么Python适合作为第一个学习的编程语言 Python语言设计的初衷就是容易
  • 神经网络之BP算法【图文并茂】

    神经网络之BP算法 图说神经网络 BP算法理论推导 例子运用 代码 最近在学习 Deep Learning 这本书 书中在前馈神经网络 全连接神经网络以及卷积神经网络等内容中 都有提到反向传播算法 这一算法可以说是神经网络中求解参数比较核心
  • 判断是当前是什么手机和判断是在微信内还是浏览器内

    判断是android还是ios switch uni getSystemInfoSync platform case android console log 运行在安卓手机上 break case ios console log 运行在io
  • web自动化如何区分在不同浏览器运行_Web自动化测试:浏览器不同页签之间的切换(handle)

    一 切换页签 句柄handle 的基础用法 备注 部分方法为老写法 官方已经不推荐使用 点击这篇文章查看切换handle新写法 1 获取浏览器当前所在页签的句柄 current window handle 2 获取所有页面窗口的句柄 win
  • 40. 实战:基于tkinter实现用户UI界面——对34小节的VIP音乐解析系统的全面升级(附源码)

    目录 前言 目的 思路 代码实现 1 首先设计主页UI界面 2 封装核心解析歌曲代码 3 下载音乐到本地 4 将界面居中 禁止修改窗口大小 等待关闭 退出指令 完整源码 运行效果 使用过程 菜单栏 打包的exe 总结 前言 本节将升级34
  • VS2005 VS2010数据断点不能设置的原因 new data breakpoint is disabled

    数据断点是VS的一项犀利功能 尤其在我司这种代码量巨大的大型系统中 想知道一个被很多地方访问的类的变量是在什么时候被改变的是一件很困难的事情 使用数据断点能极大提高debug的效率 可昨天突然遇到了数据断点按钮变灰的情况 无法进行设置 原因
  • P4197 Peaks(主席树+Kruskal重构树+倍增)

    传送门 前缀知识 Kruskal重构树 题面 思路 看到困难值小于等于x就应该想到用Kruskal重构树了 首先我们构建一颗Kruskal重构树 然后询问是问我们从某个点 u u u出发 不超过困难值 h h h 那么这个过程我们可以用倍增