# 主席树 自己的理解
题意:
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不晓得
位运算要比加减乘除要快