• 划分树学习笔记


        今天没事就去刷以前hdu做过但是没过的题,前面的题现在还是能过的,做到这题就卡了,传说中的划分树,只闻其名未见其身。然后搜索了一下划分树的资料,擦擦擦,这不就是同快排的原理+线段树的操作,两者一融合进化成了划分树么。前面两个都会,学习起来倍感轻松。

     建树过程: 先对区间[1,n]内所有元素进行排序,未排序之前的数列赋值给线段树的第一层元素(tree[0][i]),然后就是同快排的原理以排序后中间元素为参照,小于它的放在树下一层的左边,大于它的放在树下一层的右边(划分树建树以中间元素为参照,快排以第一关键元素为参照)。然后再开一个sum数组,sum[d][i]表示第d层前i个元素有多少个元素小于as[mid](as[mid]为排序后的中间值),这样一层一层建树下去最后建完树后等于对原数列排好了序。

    查询过程: 这里最关键了。在查找区间[tl,tr]时,往下查询[tl,tr]左右孩子时,都要对区间[tl,tr]进行更新。

    定义两个数s, ss,  d表示第d层, k(k表示要查询的第k元素) :

    s 表示区间[l,tl]有多少个元素小于as[mid],  s=sum[d][tl-1];

    ss 表示区间[tl,tr]有多少个元素小于as[mid], ss=sum[d][tr]-s;

    if(ss>=k)  则下一层查询区间为[l+s,l+s+ss-1];

    else  则下一层查询区间为[mid+1+tl-l-s,mid+1+tr-l-s-ss];

    自己懒,不愿画图多解释,借用一下小媛姐姐的图。

    分析一下算法复杂度: 建树nlogn+查询mlogn,很强大的说。

    题目链接:hdu2665 kth number

    题目大意:O(-1)

    解题思路:O(-1)

    View Code
     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 const int maxn=100005;
     8 int sum[20][maxn], tree[20][maxn];
     9 int a[maxn], as[maxn]; ///原数组, 排序后数组
    10 
    11 void Build(int d, int l, int r)
    12 {
    13     int mid=(l+r)>>1, lp=l, rp=mid+1, lm=mid-l+1;
    14     for(int i=l; i<=mid; i++)
    15         if(as[i]<as[mid]) lm--; ///!!! 先假设前mid-l+1个数都等于as[mid],as[i]比它小则减1
    16     for(int i=l; i<=r; i++)
    17     {
    18         if(i==l) sum[d][i]=0;   ///sum[d][i]表示第d层前i个数有多少个小于as[mid]
    19         else sum[d][i]=sum[d][i-1];
    20         if(tree[d][i]==as[mid])
    21         {
    22             if(lm) lm--, sum[d][i]++, tree[d+1][lp++]=tree[d][i];
    23             else tree[d+1][rp++]=tree[d][i];
    24         }
    25         else if(tree[d][i]<as[mid]) sum[d][i]++, tree[d+1][lp++]=tree[d][i];
    26         else tree[d+1][rp++]=tree[d][i];
    27     }
    28     if(l==r) return ;
    29     Build(d+1,l,mid);
    30     Build(d+1,mid+1,r);
    31 }
    32 
    33 int Query(int d, int l, int r, int tl, int tr, int k)
    34 {
    35     int s, ss, mid=(l+r)>>1;
    36     if(l==r) return tree[d][l];
    37     if(l==tl) s=0, ss=sum[d][tr]; ///!!特判
    38     else s=sum[d][tl-1], ss=sum[d][tr]-s;
    39     if(ss>=k) return Query(d+1,l,mid,l+s,l+s+ss-1,k);
    40     else return Query(d+1,mid+1,r,mid+1+tl-l-s,mid+1+tr-l-s-ss,k-ss);
    41 }
    42 
    43 int main()
    44 {
    45     int T, n, m;
    46     cin >> T;
    47     while(T--)
    48     {
    49         scanf("%d%d",&n,&m);
    50         for(int i=1; i<=n; i++)
    51         {
    52             scanf("%d",a+i);
    53             tree[0][i]=as[i]=a[i];
    54         }
    55         sort(as+1,as+n+1);
    56         Build(0,1,n);
    57         while(m--)
    58         {
    59             int l, r, k;
    60             scanf("%d%d%d",&l,&r,&k);
    61             int ans=Query(0,1,n,l,r,k);
    62             printf("%d\n",ans);
    63         }
    64     }
    65     return 0;
    66 }

    题目链接:hdu3743  minimum sum

    题目大意:给你一个有n个数的数列,然后有m次询问[l,r],让你在[l,r]中找一个数x使得|Xi-x|最小(l<=i<=r).

    解题思路:初中知识可知,形如|Xi-x|最小,那么必定是找排序后区间的中位数了。区间[l,r]的中位数必定是第k((r-l+2)/2)位。

    假设一段区间排序后是x1,x2,x3,x4,x5,x6,x7,x4是中位数,那么题要求的答案不就是(x4-x1)+(x4-x2)+(x4-x3)+(x5-x4)+(x6-x4)+(x7-x4)=(x5+x6+x7)-(x1+x2+x3)。   

    结果分为了比中位数大的数和比中位数小的数,啊哈,这不就是要我们求第k元素么,这个k值固定了而已(中位数)。这里需要多开一个数组tol来记录第d层前i个数的和。当查询到第d层时,如果所求的ss比k大,则结果ans加上被分到右边的数,向左下层继续查询。如果ss比k小,则结果ans减去分到左边的数,向右下层继续查询。

    注意区间奇偶判定以及数的范围。

    View Code
     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 const int maxn=100005;
     8 typedef long long lld;
     9 int sum[20][maxn], tree[20][maxn];
    10 lld tol[20][maxn];  ///!!!
    11 int a[maxn], as[maxn];
    12 
    13 void Build(int d, int l, int r)
    14 {
    15     int mid=(l+r)>>1, lp=l, rp=mid+1, lm=mid-l+1;
    16     for(int i=l; i<=r; i++)
    17     {
    18         if(as[i]<as[mid]) lm--;
    19         if(i==l) tol[d][i]=tree[d][i];
    20         else tol[d][i]=tol[d][i-1]+tree[d][i];
    21     }
    22     for(int i=l; i<=r; i++)
    23     {
    24         if(i==l) sum[d][i]=0;
    25         else sum[d][i]=sum[d][i-1];
    26         if(tree[d][i]==as[mid])
    27         {
    28             if(lm) lm--, sum[d][i]++, tree[d+1][lp++]=tree[d][i];
    29             else tree[d+1][rp++]=tree[d][i];
    30         }
    31         else if(tree[d][i]<as[mid]) sum[d][i]++, tree[d+1][lp++]=tree[d][i];
    32         else tree[d+1][rp++]=tree[d][i];
    33     }
    34     if(l==r) return ;
    35     Build(d+1,l,mid);
    36     Build(d+1,mid+1,r);
    37 }
    38 
    39 lld Query(int d, int l, int r, int tl, int tr, int k, lld &ret)
    40 {
    41     if(l==r) return tree[d][l];
    42     int mid=(l+r)>>1, s, ss, ql, qr;
    43     if(tl==l) s=0, ss=sum[d][tr];
    44     else s=sum[d][tl-1], ss=sum[d][tr]-s;
    45     int x=tl-l-s, xx=tr-tl+1-ss;
    46     if(ss>=k)
    47     {
    48         ql=mid+1+x, qr=ql+xx-1;
    49         if(xx>0) ret+=tol[d+1][qr]-(x>0?tol[d+1][ql-1]:0);
    50         return Query(d+1,l,mid,l+s,l+s+ss-1,k,ret);
    51     }
    52     else
    53     {
    54         ql=l+s, qr=l+s+ss-1;
    55         if(ss>0) ret-=tol[d+1][qr]-(s>0?tol[d+1][ql-1]:0);
    56         return Query(d+1,mid+1,r,mid+1+x,mid+1+x+xx-1,k-ss,ret);
    57     }
    58 }
    59 
    60 int main()
    61 {
    62     int n, m, T, tcase=0;
    63     cin >> T;
    64     while(T--)
    65     {
    66         scanf("%d",&n);
    67         for(int i=1; i<=n; i++)
    68         {
    69             scanf("%d",a+i);
    70             tree[0][i]=as[i]=a[i];
    71         }
    72         sort(as+1,as+n+1);
    73         Build(0,1,n);
    74         scanf("%d",&m);
    75         printf("Case #%d:\n",++tcase);
    76         while(m--)
    77         {
    78             int l, r;
    79             scanf("%d%d",&l,&r);
    80             lld ret=0, len=r-l+1;
    81             lld tp=Query(0,1,n,l+1,r+1,(len+1)/2,ret);
    82             if(len%2==0) ret-=tp;
    83             printf("%I64d\n",ret);
    84         }
    85         puts("");
    86     }
    87     return 0;
    88 }
  • 相关阅读:
    atitit.交换机 汇聚上联、网络克隆和标准共享的原理与区别
    Atitit.数据库分区的设计 attilax  总结
    Atitit.数据库分区的设计 attilax  总结
    Atitit.常用分区api的attilax总结
    Atitit.常用分区api的attilax总结
    Atitit.  单列索引与多列索引 多个条件的查询原理与设计实现
    Atitit.  单列索引与多列索引 多个条件的查询原理与设计实现
    Atitit.sql where条件表达式的原理  attilax概括
    Atitit.sql where条件表达式的原理  attilax概括
    Atitit.分区对索引的影响 分区索引和全局索引 attilax总结
  • 原文地址:https://www.cnblogs.com/kane0526/p/3033212.html
Copyright © 2020-2023  润新知