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

来源:互联网 发布:淘宝教程开店秘籍 编辑:程序博客网 时间:2024/05/20 17:42

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



1 0
原创粉丝点击