zoukankan      html  css  js  c++  java
  • 划分树学习笔记

        今天没事就去刷以前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 }
  • 相关阅读:
    ajax专题
    luogu P1346 电车 最短路
    luogu P1462 通往奥格瑞玛的道路 最短路
    luogu P1328 生活大爆炸版石头剪刀布
    luogu P1315 联合权值 枚举
    luogu P1156 垃圾陷阱 背包问题
    luogu P1217 回文质数 枚举
    luogu P3650 滑雪课程设计 枚举
    luogu1209 修理牛棚 贪心
    luogu P1223 排队接水 贪心
  • 原文地址:https://www.cnblogs.com/kane0526/p/3033212.html
Copyright © 2011-2022 走看看