hdu 4417,poj 2104 划分树(模版)归并树(模版)

来源:互联网 发布:全球黑客攻击源码 编辑:程序博客网 时间:2024/05/22 02:17

这次是彻底把划分树搞明白了,与此同时发现了模版的重要性。写程序一个字符都不能错啊~~~

划分树详解:点击打开链接

题意:求一组数列中任意区间不大于h的个数。


这个题的做法是用二分查询  求给定区间内的中值再与K进行比较。


重点介绍划分树:

数据结构:

t[20][maxn] // 树结构,划分树存储

sum[20][maxn] // 记录该行[l,i] 中i到l有多少个存在左子树中

as[maxn]  //原始数组排序后结果


#include <stdio.h>#include <string.h>#include <math.h>#include <bitset>#include <iostream>#include <algorithm>  #define inf 0x3fffffff#define mid ((l+r)>>1)const int maxn=100000+100;typedef unsigned __int64 ull;using namespace std;int N,M;int t[30][maxn]; // 树结构,划分树存储int sum[30][maxn]; // 记录该行[l,i] 中i到l有多少个存在左子树中int as[maxn];//排序后数组//建树是依据上一层建立下一层,所以t[p+1][ls++]=t[p][i]; l==r判断的条件放在循环后是为了让每个元素都到最底层void build(int p,int l,int r){int ls=l,rs=mid+1;int lm=0,i;for(i=rs-1;i>=l;i--)// ls rs 左右子树开始的位置 lm 放入左子树的中值数目(避免中值过多树不平衡)if(as[i]==as[mid]) lm++;else break;for(i=l;i<=r;i++){if(i==l) sum[p][i]=0;//sum的计算若为初始则为0,注意这里的每行sum有多个分开的树else     sum[p][i]=sum[p][i-1];if(t[p][i]==as[mid])if(lm) //将部分中值的数放入左边lm--,sum[p][i]++,t[p+1][ls++]=t[p][i];else t[p+1][rs++]=t[p][i];else if(t[p][i]<as[mid]) sum[p][i]++,t[p+1][ls++]=t[p][i];//小于中值放入左边else t[p+1][rs++]=t[p][i];//大于放入右边,sum与其无关}if(l==r) return;build(p+1,l,mid);build(p+1,mid+1,r);}/*在p层,[l,r]范围内查询[ql,qr]中第K大数l+s  跳过查询区间前放入左子树个数,l+sum[p][qr]-1  [l,qr]放入左子树的个数ql-l-s 查询区间前放入右子树的个数,qr-l-sum[p][qr] [l,qr]放入右子树的个数*/int query(int p,int l,int r,int ql,int qr,int k){if(l==r) return t[p][l];int s,ss;//s是ql左边有多少放入下层左子树,ss是[ql,qr]中有多少放入下层左子树if(ql==l)s=0,ss=sum[p][qr];elses=sum[p][ql-1],ss=sum[p][qr]-s;if(k<=ss)return query(p+1,l,mid,l+s,l+sum[p][qr]-1,k);elsereturn query(p+1,mid+1,r,mid+1+ ql-l-s,mid+1 +qr-l-sum[p][qr],k-ss);}int main(){    int T;    int n,m,cas=1;int i,l,r,k;    scanf("%d",&T);while(T--){scanf("%d%d",&N,&M);for(i=0;i<N;i++)scanf("%d",as+i),t[0][i]=as[i];sort(as,as+N);build(0,0,N-1);printf("Case %d:\n",cas++);for(i=0;i<M;i++){scanf("%d%d%d",&l,&r,&k);int mi=1,ma=r-l+1,Mid,ans=r-l+2; while(mi<=ma)              {                  Mid=(mi+ma)>>1;                  int tmp=query(0,0,N-1,l,r,Mid);                if(tmp>k)                  {                      ans=Mid;                      ma=Mid-1;                  }                  else                  mi=Mid+1;              }  printf("%d\n",ans-1);}}    return 0;}

归并树:

就是在归并过程中保存结果,因为归并排序与线段树建树类似都是自底向上,所以可以保存。(这个比划分树好理解多了)

#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 100005 using 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++];         else             mer[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;         else             high=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; } 


0 0
原创粉丝点击