• 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:
    ",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
    ",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
    ",slove(l,r,k-1)); 
            } 
        } 
        return 0; 
    } 


  • 相关阅读:
    走迷宫(三):在XX限制条件下,是否走得出。
    ELMo
    BERT
    ARNOR 远程关系监督抽取
    谷歌T5
    seq2seq
    Attention Is All You Need(Transformer)
    attention的实现
    RNN
    test
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7238742.html
Copyright © 2020-2023  润新知