• bryce1010专题训练——划分树


    1、求区间第K大
    HDU2665 Kth number

    /*划分树
    查询区间第K大
    
    */
    
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    
    #define ll long long
    const int MAXN=100009;
    int tree[21][MAXN];//表示每层每个位置的值
    int sorted[MAXN];//已经排序好的数
    int toleft[21][MAXN];//toleft[p][i]表示第i层从1到i有多少数划入左边
    
    /*建树
    same表示等于sorted[mid]的个数,初始化为mid-l+1,扫描后每出现一个更小的值,减1
    所以same表示要被分入左边等于中间值的个数
    建树分三种情况:
    1、如果tree[[dep][i]<sorted[mid]划入左边
    2、如果tree[dep][i]==sorted[mid],same>0的话划入左边
    3、否则就是划入右边
    */
    
    void build(int l,int r,int dep)
    {
        if(l==r)return;
        int mid=(l+r)>>1;
        int same=mid-l+1;
        for(int i=l;i<=r;i++)
        {
            if(tree[dep][i]<sorted[mid])
            {
                same--;
            }
        }
        int lpos=l;
        int rpos=mid+1;
        for(int i=l;i<=r;i++)
        {
            if(tree[dep][i]<sorted[mid])
                tree[dep+1][lpos++]=tree[dep][i];
            else if(tree[dep][i]==sorted[mid]&&same)
            {
                tree[dep+1][lpos++]=tree[dep][i];
                same--;
            }
            else
            {
                tree[dep+1][rpos++]=tree[dep][i];
            }
            toleft[dep][i]=toleft[dep][l-1]+lpos-l;
        }
        build(l,mid,dep+1);
        build(mid+1,r,dep+1);
    }
    /*查询操作
    查询区间第K大值,[L,R]是大区间,[l,r]是要查询的小区间
    cnt记录划入左边的个数
    1、如果cnt>=k,说明第K大在左子树
    2、否则,说明第K大在右子树
    */
    int query(int L,int R,int l,int r,int dep,int k)
    {
        if(l==r)return tree[dep][l];
        int mid=(L+R)>>1;
        int cnt=toleft[dep][r]-toleft[dep][l-1];
        if(cnt>=k)
        {
            int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
            int newr=newl+cnt-1;
            return query(L,mid,newl,newr,dep+1,k);
        }
        else
        {
            int newr=r+toleft[dep][R]-toleft[dep][r];
            int newl=newr-(r-l-cnt);
            return query(mid+1,R,newl,newr,dep+1,k-cnt);
        }
    }
    
    int main()
    {
        int n,m;
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            memset(tree,0,sizeof(tree));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&tree[0][i]);
                sorted[i]=tree[0][i];
            }
            sort(sorted+1,sorted+n+1);
            build(1,n,0);
            int s,t,k;
            while(m--)
            {
                scanf("%d%d%d",&s,&t,&k);
                printf("%d
    ",query(1,n,s,t,0,k));
            }
        }
        return 0;
    }
    
    

    2、查找中位数
    http://acm.hdu.edu.cn/showproblem.php?pid=3473【HDU3473】

    找出中间的那个x,计算公式为x*(r-l+1)/2)-(sum[l,mid]+x)+(sum[l,r]-sum[l,mid]-x)-((r-l-前一半-1+1)/2)*x

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define INF 1000000001
    #define MOD 1000000007
    #define ll long long
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define pi acos(-1.0)
    using namespace std;
    const int MAXN = 100010;
    int num[20][MAXN],cnt[20][MAXN],sor[MAXN],n,leftnum;
    ll sum[20][MAXN],leftsum,all[MAXN];//sum记录第d层 第i个数之前小于sor[m]的和
    void build(int l,int r,int d)
    {
        if(l == r){
            return ;
        }
        int m = (l + r) >> 1;
        int same_m = m - l + 1;
        for(int i = l; i <= r; i++){
            if(num[d][i] < sor[m])same_m --;
        }
        int cnt_small = 0;
        int pl,pr;
        ll val = 0;
        pl = l,pr = m + 1;
        for(int i = l; i <= r; i++){
            if(num[d][i] < sor[m]){
                cnt_small ++;
                val += num[d][i];
                sum[d][i] = val;
                cnt[d][i] = cnt_small;
                num[d+1][pl++] = num[d][i];
            }
            else if(num[d][i] == sor[m] && same_m){
                same_m --;
                cnt_small ++;
                val += num[d][i];
                sum[d][i] = val;
                cnt[d][i] = cnt_small;
                num[d+1][pl++] = num[d][i];
            }
            else {
                sum[d][i] = val;
                cnt[d][i] = cnt_small;
                num[d+1][pr++] = num[d][i];
            }
        }
        build(l,m,d+1);
        build(m+1,r,d+1);
    }
    ll query(int L,int R,int k,int l,int r,int d)
    {
        if(l == r){
            return num[d][l];
        }
        int m = (l + r) >> 1;
        int s,ss;
        ll val = 0;
        if(l == L)s = 0, val = sum[d][R];
        else s = cnt[d][L-1], val = sum[d][R] - sum[d][L-1];
        ss = cnt[d][R] - s;
        if(ss >= k){
            int newl = l + s;
            int newr = l + s + ss - 1;
            return query(newl,newr,k,l,m,d+1);
        }
        else {
            leftnum += ss;//进入左孩子不用处理,进入右孩子时 就要加上左孩子的值
            leftsum += val;
            int a = L - l - s;
            int b = R - L + 1 - ss;
            int newl = m + 1 + a;
            int newr = m + 1 + a + b - 1;
            return query(newl,newr,k - ss,m+1,r,d+1);
        }
    }
    int main()
    {
        int t,ff = 0;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            memset(all,0,sizeof(all));
            memset(num,0,sizeof(num));
            for(int i = 1; i <= n; i++){
                scanf("%d",&num[1][i]);
                sor[i] = num[1][i];
                all[i] = all[i-1] + sor[i];
            }
            sort(sor + 1,sor + n + 1);
            build(1,n,1);
            int q,x,y;
            scanf("%d",&q);
            printf("Case #%d:
    ",++ff);
            while(q--){
                scanf("%d%d",&x,&y);
                x += 1;
                y += 1;
                int len = (y - x + 1);
                ll tp;
                leftnum = 0;
                leftsum = 0;
                if(len % 2){
                    int k = (len + 1) >> 1;
                    tp = query(x,y,k,1,n,1);
                }
                else {
                    int k = len >> 1;
                    tp = query(x,y,k,1,n,1);
                }
                //cout<<tp<<‘ ‘<<leftnum<<‘ ‘<<leftsum<<‘ ‘<<all[y]<<‘ ‘<<all[x+leftnum]<<endl;
                ll ans = tp * (leftnum + 1) - (leftsum + tp) + (all[y] - all[x - 1] - (leftsum + tp)) - (y - x + 1 - (leftnum + 1)) * tp;
                printf("%lld
    ",ans);
            }
            printf("
    ");
        }
        return 0;
    }
  • 相关阅读:
    c# 中的线程和同步
    Javascript 观察者模式
    连接SQLite 创建ADO.net实体类
    给软件增加注册功能 c#
    log4net 使用步骤
    C# 操作 Excel
    PCL编译历程
    设计模式
    kinect
    eclipse配置servlet错误
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9386857.html
Copyright © 2020-2023  润新知