题意:区间静态第K大。
题解:
可持久化线段树。
可持久化线段树:
基本思想:我们维护插入每个节点后的线段树。
朴素写法(MLE+TLE)我们对于每次插入,都复制一棵线段树而后插入,这样保证了“可持久化”。
但是显然,时间复杂度和空间复杂度都是n^2的。233。
所以有了优化写法:我们发现每次的插入只有logn个节点被改变,所以只需要这些点新建,其它点都指向原来版本的节点就好了。
空间复杂度nlogn。
然后这道题是区间第K大,对于区间[a,b],插入到b时的线段树,节点的size-(a-1)时节点的size,显然就是[a,b]这个区间中插入数值后的结果,根据这个可以进行查询。
代码太水了,不懂可以自己看代码。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define LOGN 20
#define inf 0x3f3f3f3f
#define ls s[x].son[0]
#define rs s[x].son[1]
using namespace std;
int pos[N],n,m;
int src[N],rank;
int crs[N];
struct LSH
{
int x,id;
bool operator < (const LSH &a)const{return x<a.x;}
}lsh[N];
int cnt;
struct Functional_Segment_Tree
{
int son[2];
int size,l,r;
}s[N*LOGN];
inline void pushup(int x)
{
s[x].size=s[ls].size+s[rs].size;
}
int build(int last,int l,int r,int d)
{
int x=++cnt;
s[x].size=s[last].size,s[x].l=l,s[x].r=r;
if(l==r){s[x].size++;return x;}
int mid=l+r>>1;
if(d<=mid)ls=build(s[last].son[0],l,mid,d),rs=s[last].son[1];
else rs=build(s[last].son[1],mid+1,r,d),ls=s[last].son[0];
pushup(x);
return x;
}
void query(int last,int now,int l,int r,int k)
{
if(l==r){printf("%d\n",crs[l]);return ;}
int mid=l+r>>1,temp=s[s[now].son[0]].size-s[s[last].son[0]].size;
if(temp<k)query(s[last].son[1],s[now].son[1],mid+1,r,k-temp);
else query(s[last].son[0],s[now].son[0],l,mid,k);
}
int main()
{
// freopen("test.in","r",stdin);
int i,a,b,c;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&lsh[i].x),lsh[i].id=i;
sort(lsh+1,lsh+n+1);
src[lsh[1].id]=++rank,crs[rank]=lsh[1].x;
for(i=2;i<=n;i++)
{
if(lsh[i].x!=lsh[i-1].x)crs[++rank]=lsh[i].x;
src[lsh[i].id]=rank;
}
for(i=1;i<=n;i++)
pos[i]=build(pos[i-1],1,rank,src[i]);
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
query(pos[a-1],pos[b],1,n,c);
}
return 0;
}