主席树板子(递增序列)

第k大的数

描述
你为Macrohard公司的数据结构部门工作,你的工作是重新写一个数据结构,这个数据结构能快速地找到一段数列中第k大的数。

就是说,给定一个整数数列a[1…n],其中每个元素都不相同,你的程序要能回答一组格式为Q (i , j , k)的查询,Q(i, j ,k)的意思是“在a[i…j]中第k大的数是多少?”

例如令 a = {1, 5, 2, 6, 3, 7, 4},查询格式为Q (2 , 5 , 3),数列段a[2…5] = {5, 2, 6, 3},第3大的数是5,所以答案是5。

输入
文件第一行包括一个正整数n,代表数列的总长度,还有一个数m,代表有m个查询。 n,m满足:1≤n≤100 000, 1≤m≤5 000 第二行有n个数,代表数列的元素,所有数都不相同,而且不会超过109 接下来有m行,每行三个整数i , j , k,代表一次查询, i , j , k满足1≤i≤j≤n, 1≤k≤j − i + 1

输出
输出每个查询的答案,用换行符隔开

样例输入 [复制]
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
样例输出 [复制]
5
6
3

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
const int N=1e5+10;
int L[N<<5],R[N<<5],T[N],sum[N<<5];
int n,m,tot=0;
int a[N],w[N];
inline int  build(int l,int r){
	int rt=++tot;sum[rt]=0;
	if(l<r){
		int mid=(l+r)>>1;
		L[rt]=build(l,mid);
		R[rt]=build(mid+1,r);
	}
	return rt;
}
inline int  update(int pre,int l,int r,int x){
	int rt=++tot;
	L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+1;
	if(l<r){
		int mid=(l+r)>>1;
		if(x<=mid){
		    L[rt]=update(L[pre],l,mid,x);
		}
		else{
			R[rt]=update(R[pre],mid+1,r,x);
		}
	}
	return rt;
}
inline int que(int prel,int prer,int l,int r,int k){
	if(l>=r) return l;
	int mid=(l+r)>>1;
	int num=sum[L[prer]]-sum[L[prel]];
	if(num>=k) return que(L[prel],L[prer],l,mid,k);
	else return que(R[prel],R[prer],mid+1,r,k-num);
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
    	a[i]=read();w[i]=a[i];
    }
	sort(w+1,w+n+1);
	int s1=unique(w+1,w+n+1)-w-1;
	T[0]=build(1,s1);
	for(int i=1;i<=n;i++){
		int x=lower_bound(w+1,w+s1+1,a[i])-w;
	   T[i]=update(T[i-1],1,s1,x);	 
	}
	while(m--){
		int l=read(),r=read(),k=read();
		int x=que(T[l-1],T[r],1,s1,k);
		printf("%d\n",w[x]);
	}
	return 0;
}
提供的引用内容中未涉及C++虚板子题相关信息。在常见的算法竞赛平台上能找到C++虚板子题。以下是一些推荐: - **洛谷P2495 [SDOI2011]消耗战**:题目要求在一棵上,给出若干询问,每次询问指定一些关键点,要切断从根节点到所有关键点的路径,求最小的边权和。此问题适合用虚来解决,通过构建虚,能将每次询问的时间复杂度降低。 - **Codeforces 613D Kingdom and its Cities**:该题给出一棵,多次询问一些点的集合,要判断能否通过删除一些点,使得集合内任意两点不连通,若可以,求最少删除点数。借助虚可以高效处理每次询问。 ```cpp // 以下是一个简单的虚构建的伪代码示例 #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 100005; vector<int> G[MAXN]; int dep[MAXN], fa[MAXN], top[MAXN], son[MAXN], sz[MAXN]; // 链剖分相关数组 // 链剖分初始化 void dfs1(int u, int f) { fa[u] = f; dep[u] = dep[f] + 1; sz[u] = 1; for (int v : G[u]) { if (v == f) continue; dfs1(v, u); sz[u] += sz[v]; if (sz[v] > sz[son[u]]) son[u] = v; } } void dfs2(int u, int t) { top[u] = t; if (son[u]) dfs2(son[u], t); for (int v : G[u]) { if (v == fa[u] || v == son[u]) continue; dfs2(v, v); } } // 求LCA int lca(int u, int v) { while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) swap(u, v); u = fa[top[u]]; } return dep[u] < dep[v] ? u : v; } // 构建虚 vector<int> vt[MAXN]; bool cmp(int u, int v) { return dep[u] < dep[v]; } void build_virtual_tree(vector<int> &key_points) { sort(key_points.begin(), key_points.end(), cmp); vector<int> stk; stk.push_back(1); // 根节点 for (int u : key_points) { if (u == 1) continue; int l = lca(u, stk.back()); while (1) { if (dep[l] >= dep[stk[stk.size() - 2]]) { if (l != stk.back()) { vt[l].push_back(stk.back()); if (l != stk[stk.size() - 2]) stk.push_back(l); } break; } vt[stk[stk.size() - 2]].push_back(stk.back()); stk.pop_back(); } stk.push_back(u); } while (stk.size() > 1) { vt[stk[stk.size() - 2]].push_back(stk.back()); stk.pop_back(); } } int main() { // 这里可以添加读取的边和关键点的代码 vector<int> key_points = {2, 3, 4}; // 示例关键点 build_virtual_tree(key_points); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值