学习报告:主席树真的nb,如果朴素做要开n个线段树,内存爆炸,优化的方式,每次插入一个数,并不是整个权值线段树都改变了,只是改变logn个节点,我们只需要多开logn个节点就OK,用root数组记录历史个线段树版本,询问区间,可以做相减,询问第k小值,与左树的所有节点sum作比较,如果小就在左边,否则在右边,在右边的时候注意是k-sum,由于值域太大,可以使用离散化,获得每个数的排名,可以把值域缩小到n。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<set>
#define IL inline
#define x first
#define y second
typedef long long ll;
using namespace std;
const int N=200010;
struct node{
int l;
int r;
int sum;
}tr[N*40];
int root[N];
int w[N],cnt;
vector<int>v;
int getid(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void modify(int l,int r,int pre,int &now,int k)
{
tr[++cnt]=tr[pre];
now=cnt;
tr[now].sum++;
if(l==r) return ;
int mid=l+r>>1;
if(k<=mid) modify(l,mid,tr[pre].l,tr[now].l,k);
else modify(mid+1,r,tr[pre].r,tr[now].r,k);
}
int query(int l,int r,int L,int R,int k)
{
if(l==r)
return l;
int sum=tr[tr[R].l].sum-tr[tr[L].l].sum;
int mid=l+r>>1;
if(k<=sum) return query(l,mid,tr[L].l,tr[R].l,k);
else return query(mid+1,r,tr[L].r,tr[R].r,k-sum);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>w[i];
v.push_back(w[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++)
{
modify(1,n,root[i-1],root[i],getid(w[i]));
}
while(m--)
{
int l,r,k;
cin>>l>>r>>k;
cout<<v[query(1,n,root[l-1],root[r],k)-1]<<endl;
}
return 0;
}