K-th Number(主席树)

		# 主席树 自己的理解

题意:
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
7个数字 3次询问, 7个数字分别是 1 5 2 6 3 7 4
三次询问分别:
从第二个到第五个数字中间 排第三个多的数字是;
以此类推
一定要先了解权值 1线段树 2离散化

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstring>
#include<string.h>
//头文件
using namespace std;
const int maxn = 1e5+5;
int n, m, x, y, k, cnt;
int a[maxn], root[maxn];
//a数组是先存放数字  root数组来建立树的 具体不太清楚。
vector<int>vec;

struct Node
{
 int l, r, sum;
 //l是左子树,r是右子树,sum是当前的权值
}A[maxn*40];
//我看的视频都是开40倍 也可以50倍或者32倍都有的

int getid(int x)
//得到位置 离散化的第二步
{
 return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
}


void updata(int l, int r, int &now, int pre, int pos)
{
//每一个建立新的节点都要以前一个的节点 建立当前节点 
 //从l到r 
 A[++cnt] = A[pre];
 //新分配一个内存池cnt 
 now = cnt;
 //当前节点编号变成新的内存池编号 
 A[now].sum ++;
 //由于新插入一个数 所以权值增加
 if(l == r) return;
 int mid =  (r+l) >> 1;
 if(mid >= pos) 
 //若插入的位置大于mid ,pos是要插入的数也是要插入的位置 
 updata(l,mid,A[now].l,A[pre].l,pos);
 //若插入在右边就以前一个的右子树为依托建立当前右子树 
 else
 updata(mid+1,r,A[now].r,A[pre].r,pos); 
 //若插入在左边就用前一个的左子树为依托建立当前要建立的左子树 
}



int query(int l, int r, int x, int y, int pos)
{
 // x是原来的y是现在的 
 //从l到r维护当前节点的区间 
 if(l == r) return r;
 int mid = r + l >> 1;
 int m = A[A[y].l].sum - A[A[x].l].sum;
 //左子树有多少个数 
 //因为前面插入之前 没有树,一边插入一边建树 所以之前为0? 
 if(m >= pos)
 return query(l, mid, A[x].l, A[y].l, pos);
 else
 return query(mid+1, r,A[x].r, A[y].r, pos-m);
 } 

int main()
{
 scanf("%d%d",&n,&m);
 for(int i = 1; i <= n; i ++)
 {
  scanf("%d",&a[i]);
  vec.push_back(a[i]);
 }
 
 sort(vec.begin(),vec.end());
 vec.erase(unique(vec.begin(),vec.end()),vec.end());
 //离散化 
 for(int i = 1; i <= n; i ++)
 {
  updata(1,n,root[i],root[i-1],getid(a[i]));
 }
 for(int i = 1; i <= m; i ++)
 {
  scanf("%d%d%d",&x,&y,&k);
  printf("%d\n",vec[query(1,n,root[x-1],root[y],k)-1]);
 }
 return 0;
}

我还是不太理解root数组与树的关系。
离散化就是 排序,去重
就这两步 不知道可不可以用set不晓得
位运算要比加减乘除要快


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值