POJ 2104 K-th Number 归并树与划分树

来源:互联网 发布:目前网络饰品市场如何 编辑:程序博客网 时间:2024/05/16 11:55

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

第一次接触,自己也没啥好总结的,都是看网上的资料。

归并树是在建树的过程中保存归并排序。

划分树是在建树的过程中保存快速排序。

其中归并树适合解决一个数在某个区间的名次。

划分树适合解决某个区间的K大数。

POJ这题是找K大数,归并树也可做,二分答案,再由归并树找出这个数的名次。划分树更快。

对于HDU 2665,我写的归并树总之是过不去。


归并树:

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<map>#include<stack>#include<algorithm>#include<string>#define LL long long#define LD long double#define eps 1e-7#define inf 1<<30#define MOD 1000000007#define N 100005using namespace std;struct MergeTree{int left,right,mid;}tree[N*4];int num[N],mer[20][N];int n,q;void create(int step,int l,int r,int deep){tree[step].left=l;tree[step].right=r;tree[step].mid=(l+r)>>1;if(l==r){mer[deep][l]=num[l];return;}create(step<<1,l,(l+r)/2,deep+1);create((step<<1)|1,(l+r)/2+1,r,deep+1);int i=l,j=(l+r)/2+1,p=l;//归并排序,在建树的时候保存while(i<=(l+r)/2&&j<=r){if(mer[deep+1][i]>mer[deep+1][j])mer[deep][p++]=mer[deep+1][j++];elsemer[deep][p++]=mer[deep+1][i++];}while(i<=(l+r)/2)mer[deep][p++]=mer[deep+1][i++];while(j<=r)mer[deep][p++]=mer[deep+1][j++];}int query(int step,int l,int r,int deep,int key){if(tree[step].right<l||tree[step].left>r)return 0;if(tree[step].left>=l&&tree[step].right<=r)//找到key在排序后的数组中的位置return lower_bound(&mer[deep][tree[step].left],&mer[deep][tree[step].right]+1,key)-&mer[deep][tree[step].left];return query(2*step,l,r,deep+1,key)+query(2*step+1,l,r,deep+1,key);}int slove(int l,int r,int k){int high=n,low=1,mid;//二分答案while(low<high){mid=(low+high+1)>>1;int cnt=query(1,l,r,1,mer[1][mid]);if(cnt<=k)low=mid;elsehigh=mid-1;}return mer[1][low];}int main(){while(scanf("%d%d",&n,&q)!=EOF){for(int i=1;i<=n;i++)scanf("%d",&num[i]);create(1,1,n,1);while(q--){int l,r,k;scanf("%d%d%d",&l,&r,&k);printf("%d\n",slove(l,r,k-1));}}return 0;}

划分树:

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#define N 100005using namespace std;struct Node{int left,right,mid;}tree[N*4];int sa[N],num[20][N],cnt[20][N]; //sa中是排序后的,num记录每一层的排序结果,cnt[deep][i]表示第deep层,前i个数中有多少个进入左子树int n,q;void debug(int d){for(int i=1;i<=n;i++)printf("%d ",num[d][i]);printf("\n");}void bulid(int step,int l,int r,int deep){tree[step].left=l;tree[step].right=r;tree[step].mid=(l+r)>>1;if(l==r)return;int mid=(l+r)>>1;int mid_val=sa[mid],lsum=mid-l+1;;for(int i=l;i<=r;i++)if(num[deep][i]<mid_val)lsum--;    //lsum表示左子树中还需要多少个中值int L=l,R=mid+1;for(int i=l;i<=r;i++){if(i==l)cnt[deep][i]=0;elsecnt[deep][i]=cnt[deep][i-1];if(num[deep][i]<mid_val||(num[deep][i]==mid_val&&lsum>0)){  //左子树num[deep+1][L++]=num[deep][i];cnt[deep][i]++;if(num[deep][i]==mid_val)lsum--;}elsenum[deep+1][R++]=num[deep][i];}//debug(deep);bulid(2*step,l,mid,deep+1);bulid(2*step+1,mid+1,r,deep+1);}int query(int step,int l,int r,int k,int deep){if(l==r)return num[deep][l];int s1,s2;   //s1为[tree[step].left,l-1]中分到左子树的个数if(tree[step].left==l)s1=0;elses1=cnt[deep][l-1];s2=cnt[deep][r]-s1;   //s2为[l,r]中分到左子树的个数if(k<=s2)   //左子树的数量大于k,递归左子树return query(2*step,tree[step].left+s1,tree[step].left+s1+s2-1,k,deep+1);int b1=l-1-tree[step].left+1-s1;  //b1为[tree[step].left,l-1]中分到右子树的个数int b2=r-l+1-s2;   //b2为[l,r]中分到右子树的个数return query(2*step+1,tree[step].mid+1+b1,tree[step].mid+1+b1+b2-1,k-s2,deep+1);}int main(){;while(scanf("%d%d",&n,&q)!=EOF){for(int i=1;i<=n;i++){scanf("%d",&num[1][i]);sa[i]=num[1][i];}sort(sa+1,sa+n+1);bulid(1,1,n,1);while(q--){int l,r,k;scanf("%d%d%d",&l,&r,&k);printf("%d\n",query(1,l,r,k,1));}}return 0;}


0 0
原创粉丝点击