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

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

划分树具体解释:点击打开链接

题意:求一组数列中随意区间不大于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];
	else
		s=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);
	else
		return 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; 
} 


posted @ 2017-07-26 11:34  lytwajue  阅读(178)  评论(0编辑  收藏  举报