首页 > 代码库 > 【POJ2104】K-th Number 主席树?函数式线段树?可持久化线段树?……反正是其中一个

【POJ2104】K-th Number 主席树?函数式线段树?可持久化线段树?……反正是其中一个

题意:区间静态第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;
}



【POJ2104】K-th Number 主席树?函数式线段树?可持久化线段树?……反正是其中一个