标签: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 主席树?函数式线段树?可持久化线段树?……反正是其中一个
标签:poj2104 k-th number 主席树 函数式线段树 可持久化线段树
原文地址:http://blog.csdn.net/vmurder/article/details/41889343