刚学主席树的蒟蒻求调
查看原帖
刚学主席树的蒟蒻求调
700151
_coastline_楼主2023/11/25 22:05

RT,悬赏2关

代码中写了许多的注释方便理解,感谢大佬指点一下QAQ

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define For(i , j , k) for(int i = j; i <= k; i ++)
#define MaxN 200005

struct Node{                            //节点
	int l , r;//该区间的左右端点
	int cnt;  //该区间的数的数量
}tr[MaxN * 22];
int a[MaxN] , b[MaxN];                  //原数组与去重后的数组
int root[MaxN] , len = 0 , lenb;        //记录根节点数组(每个版本的根节点),结点数量,去重后数组长度
int n , m;
//建树过程 注意:这里建的是空树,作为最开始的情况(版本0)
void Build(int op , int l , int r)
{
	int mid = (l + r) >> 1;
	tr[ ++ len ].l = l;                 //初始化
	tr[ len ].r = r;
	tr[ len ].cnt = 0;
	if(l == r) return ;
	//二分建树
	Build(op * 2 , l , mid);
	Build(op * 2 + 1 , mid + 1 , r);
}
//插入操作:对于新加入的数,只对于包括它的区间节点进行复制新节点操作,省去了空间
//参数:被复制的编号;新节点的编号;范围左、右边界;插入的数
void insert(int x , int &y , int l , int r , int val)
{                                       //插入操作(建立新版本)
	int mid = (l + r) >> 1;
	y = ++len;                          //复制一个与先前相同的节点,作为新版本
	tr[y] = tr[x];
	tr[y].cnt ++;                       //此时该区间的数的数量+1
	if(l == r) return ;                 //递归到原区间就返回
	//二分查找下一个需要操作的区间
	if(val <= mid)                      
		insert(tr[x].l , tr[y].l , l , mid , val);
	else
		insert(tr[x].r , tr[y].r , mid + 1 , r , val);
}
//查询操作:查询[l,r]的信息=[1,r]的信息-[1,l-1]的信息,再通过[l,r]中两个子儿子的数量来二分查找
//参数:第l-1个版本的编号;第r个版本的编号;范围左右边界;查询第k小
int query(int x , int y , int l , int r , int k)
{
	if(l == r) return l;
	
	int mid = (l + r) >> 1;
	int sum = tr[y].cnt - tr[x].cnt;        //算出[l,r]区间的数个数
	if(k <= sum)                            //判断第k小在哪个区间
		return query(tr[x].l , tr[y].l , l , mid , k);
	else
		return query(tr[x].r , tr[y].r , mid + 1 , r , k - sum);
}
//查询离散化后数的新下标:二分查找
int getid(int x) {return lower_bound(b + 1 , b + lenb + 1 , x) - b;}

int main()
{
	cin >> n >> m;
	Build(1 , 1 , n);
	For(i , 1 , n)          //输入数组
	{
		cin >> a[i];
		b[i] = a[i];        //b数组查询备用
	}
	//去重三部曲
	sort(b + 1 , b + 1 + n);                    //排序
	lenb = unique(b + 1 , b + 1 + n) - b - 1;   //去重
    //获取下标:getid()
	For(i , 1 , n)                              //对每个数进行插入操作
		insert(root[i - 1] , root[i] , 1 , lenb , getid(a[i]));
	
	//查询操作
    while(m --) 
	{
		int x , y , z , ans;
		cin >> x >> y >> z;
		ans = query(root[x - 1] , root[y] , 1 , lenb , z);
		cout << b[ans] << endl;
	}

    system("pause");
	return 0;
}
2023/11/25 22:05
加载中...