zoukankan      html  css  js  c++  java
  • 数状数组

      树状数组不用多讲了,网上有关于这方面的内容很多,找找都是一大片,而且也不是很难,去学一下就可以很快领悟,至于怎么用,这就需要大量的做题了!

    先提个注意点,由于Lowbit(0) = 0,这会导致x递增的那条路径发生死循环,所有当树状数组中可能出现0时,我们都全部加一,这样可以避免0带来的麻烦~~

    简单:

          POJ 2299 Ultra-QuickSort
          http://acm.pku.edu.cn/JudgeOnline/problem?id=2299
          求逆序数,可以用经典的归并排序做,也是基本的树状数组题目。

          因为a[i]比较大,但只有最多500000个,所以要离散化。

          逆序数就是求前面的数比后面的数大的次数。从后往前看就是求后面比前小的数的个数。用树状数组!c[i]表示比i的值小的个数。

    代码
    #include <iostream>
    #include
    <algorithm>
    using namespace std;
    int n;
    struct node
    {
    int num;
    int set;
    }a[
    500005];
    int hash[500005];
    __int64 c[
    500005];
    int cmp(node b,node c)
    {
    return c.num > b.num;
    }
    int Lowbit(int t)
    {
    return (-t) & t;
    }
    __int64 Sum(
    int end)
    {
    __int64 sum
    = 0;
    while(end>0)
    {
    sum
    += c[end];
    end
    -= Lowbit(end);
    }
    return sum;
    }
    void Modity(int pos,int add)
    {
    while(pos <= n)
    {
    c[pos]
    += add;
    pos
    += Lowbit(pos);
    }
    }
    int main()
    {
    int i;
    while(scanf("%d",&n)!=EOF && (n != 0))
    {
    for(i=0;i<=n;i++)
    c[i]
    = 0;
    for(i=1;i<=n;i++)
    {
    scanf(
    "%d",&a[i].num);
    a[i].
    set = i;
    }
    sort(a
    +1,a+1+n,cmp);
    for(i=1;i<=n;i++)
    {
    hash[a[i].
    set] = i;
    }
    __int64 sum
    = 0;
    for(i=n;i>=1;i--)
    {
    sum
    += Sum(hash[i]);
    Modity(hash[i],
    1);
    }
    printf(
    "%I64d\n",sum);

    }
    return 0;
    }

     

      POJ 2352 Stars

      http://acm.pku.edu.cn/JudgeOnline/problem?id=2352
          题目意思就是求每个星星左下方的星星的个数,由于y轴已经排序好了,我们可以直接用按x轴建立一维树状数组,然后求相当于它前面比它小的个数,模板直接一套就搞定了~~

     

      POJ 1195 Mobile phones
          
    http://acm.pku.edu.cn/JudgeOnline/problem?id=1195
          二维的树状数组,直接把Update()和Getsum()改为二维即可。
          如Update()函数改为:
          void Update(int x, int y, int d)
          { // 注意:当i = 0,0 + Lowbit(0) = 0,会造成死循环!
              int i, j;
              for (i = x; i < maxn; i += Lowbit(i)) // 注意这里是maxn,是tree[]的大小.
              {
                    for (j = y; j < maxn; j += Lowbit(j))
                    {
                        tree[i][j] += d;
                    }
              }
           }

           写这题的好时候就是求区间和时应该是(x2,y2)-(x2,y1-1)-(x1-1,y2)+(x1,y1)。而不是(x2,y2)-(x2,y1)-(x1,y2)+(x1,y1).

     

       POJ 2481 Cows
          
    http://acm.pku.edu.cn/JudgeOnline/problem?id=2481
           将E从打到小排序,如果E相等按S排序,然后就跟POJ 2352 Stars做法一样了~~

          

       POJ 3067 Japan
           
    http://acm.pku.edu.cn/JudgeOnline/problem?id=3067
           先按第一个坐标排序从大到小排序,如果相等按第二个坐标从大到小排序,然后就又是跟Cows和Stars做法相同了...

           做的时候有两点注意:1,算逆序的时候相同的不应该算,即求和时应该减一。

                                     2,要用__int64

           

       POJ 2029 Get Many Persimmon Trees
            
    http://acm.pku.edu.cn/JudgeOnline/problem?id=2029
            O(n ^ 2)枚举起点,再用二维树状数组求其中的点数即可。

     

    中等:

          POJ 2155 Matrix
          
    http://acm.pku.edu.cn/JudgeOnline/problem?id=2155
          经典树状数组题目,分析见前一篇文章(树状数组学习系列1 之 初步分析——czyuan原创)~~

         

      POJ 3321 Apple Tree
        
    http://acm.pku.edu.cn/JudgeOnline/problem?id=3321
          这题的难点不在于树状数组,而是如果将整棵树映射到数组中。我们可以用DFS()改时间戳的方法,用begin[i]表示以i为根的子树遍历的第一个点,end[i]表示以i为根的子树遍历的最后一个点。
          比如数据为:
          5
          1 2
          2 5
          2 4
          1 3
          那么begin[] = {1, 2, 5, 4, 3}, end[] = {5, 4, 5, 4, 3},下标从1开始。
          对于每个点都对应一个区间(begin[i], end[i]),如果要改变点a的状态,只要Update(begin[a]),要求该子树的苹果树,即Getsum(begin[a] ) - Getsum(end[a] + 1),(注:这里求和是求x递增的路径的和。)

         

      POJ 1990 MooFest
          
    http://acm.pku.edu.cn/JudgeOnline/problem?id=1990
          这题的难点是要用两个一维的树状数组,分别记录在它前面横坐标比它小的牛的个数,和在它前面横坐标比它小的牛的横坐标之和。
          按音量排个序,那么式子为:
          ans += 1LL * cow[i].volumn * (count * x - pre + total - pre - (i - count) * x);
          cow[i].volumn为该牛的能够听到的音量。
          count为在第i只牛前面横坐标比它小的牛的个数。
          pre为在第i只牛前面横坐标比它小的牛的横坐标之和。
          total 表示前i - 1个点的x坐标之和。
          分为横坐标比它小和横坐标比它大的两部分计算即可。

         

      Hdu 3015 Disharmony Trees
          http://acm.hdu.edu.cn/showproblem.php?pid=3015
          跟上题方法相同,只要按它的要求离散化后,按高度降序排序,套用上题二个树状数组的方法即可。

      Hdu 2852 KiKi's K-Number
        
    http://acm.hdu.edu.cn/showproblem.php?pid=2852
         这题与上面那题类似,只是要求比a大的第k大的数,那我们用Getsum(a)求出小于等于a的个数,那么就是要我们求第k + Getsum(a)大的数,而删除操作只要判断Getsum(a) – Getsum(a - 1)是否为0,为0则说明a不存在。

    难题:

      POJ 2464 Brownie Points II
         
    http://acm.pku.edu.cn/JudgeOnline/problem?id=2464
     

        这道题用二分也可以做的,这里介绍下树状数组的做法。首先有n个点,过每个点可以做x,y轴,把平面切成BL, TL, TR, BR四个部分,我们现在的问题是如果快速的计算这四个部分的点的个数。
          这样我们可以先预处理,先按y坐标排序,求出每个点正左方和正右方的点的个数LeftPoint[], RightPoint[],复杂度为O(n),同样我们再以x坐标排序,求出每个点正上方和正下方点的个数UpPoint[], DownPoint[]。还要求出比点i y坐标大的点的个数 LageY[]。注意:这里要进行下标映射,因为两次排序点的下标是不相同的。
          然后按x坐标从小到大排序,x坐标相等则y坐标从小到大排序。我们可以把y坐标放在一个树状数组中。
    对于第i个点,求出Getsum(y[i])即为BL的个数,然后Update(y[i])。由于现在是第i点,说明前面有i – 1个点, 那么
         TL = i - 1 - LeftPoint[i]- BL;
         TR = LargeY[i] - TL – UpPoint[i] ;
         BR = n - BL - TL - TR - LeftPoint[i] - RightPont[i] - UpPoint[i] – DownPont[i] - 1;
         这样我们就求出四个部分的点的个数,然后判断有没有当前解优,有的话就更新即可~~

    代码
    #include <iostream>
    #include
    <algorithm>
    using namespace std;
    #define maxn 200005
    struct node
    {
    int x;
    int y;
    int LeftPoint; //正左边的点数
    int RightPoint; //正右边的点数
    int UpPoint; //正上方的点数
    int DownPoint; //正下方的点数
    int LargeY; //比该点Y大的数的数目
    }p[maxn];
    int C[maxn];
    int BL,TL,TR,BR;
    /*对第j个点的四块内的点数
    BL = Getsum(p[j].y) - p[j].LeftPoint - p[j].DownPoint;
    TL = j - p[j].LeftPoint - p[j].DownPoint - BL;
    TR = p[j].LargeY - TL - p[j].UpPoint;
    BR = n - j - 1 - TR - p[j].RightPoint - p[j].UpPoint;
    */
    struct node1
    {
    int Stan;
    int Ollie;
    }V[maxn];
    bool cmp(node a,node b)
    {
    if(a.x == b.x)
    return a.y < b.y;
    return a.x < b.x;
    }
    bool cmp2(node a,node b)
    {
    if(a.y == b.y)
    return a.x < b.x;
    return a.y < b.y;
    }
    bool cmp3(node1 a,node1 b)
    {
    if(a.Stan == b.Stan)
    return a.Ollie < b.Ollie;
    return a.Stan > b.Stan;
    }
    //bool cmp1(node a,node b){return a.x < b.x;}

    int lowb(int t)
    {
    return t&(-t);
    }
    void Updata(int i)
    {
    while(i<maxn)
    {
    C[i]
    += 1;
    i
    += lowb(i);
    }
    }
    int Getsum(int i)
    {
    int s = 0;
    while(i>0)
    {
    s
    += C[i];
    i
    -= lowb(i);
    }
    return s;
    }
    int main()
    {
    int i,j,k;
    int n,num;
    while(scanf("%d",&n)!=EOF && n!=0)
    {
    memset(C,
    0,sizeof(C));
    for(i=0;i<n;i++)
    {
    scanf(
    "%d%d",&p[i].x,&p[i].y);
    }
    sort(p,p
    +n,cmp); //X轴离散化
    for(num=1,i=0;i<n;num++)
    {
    for(j=i+1;j<n;j++)
    {
    if(p[i].x != p[j].x)
    break;
    }
    for(k=i;k<j;k++)
    {
    p[k].UpPoint
    = j-1-k;
    p[k].DownPoint
    = k - i;
    p[k].x
    = num;
    }
    i
    = j;
    }
    sort(p,p
    +n,cmp2);//Y轴离散化
    for(num=1,i=0;i<n;num++)
    {
    for(j=i+1;j<n;j++)
    {
    if(p[i].y != p[j].y)
    break;
    }
    for(k=i;k<j;k++)
    {
    p[k].LeftPoint
    = k - i;
    p[k].RightPoint
    = j-1-k;
    p[k].y
    = num;
    p[k].LargeY
    = n - j;
    }
    i
    = j;
    }
    sort(p,p
    +n,cmp);
    for(num=0,i=0;i<n;num++)
    {
    int Stan_num = 0x7fffffff;
    int Ollie_num = 0;
    for(j=i;j<n;j++)
    {
    if(p[j].x != p[i].x)
    break;
    BL
    = Getsum(p[j].y) - p[j].LeftPoint - p[j].DownPoint;
    TL
    = j - p[j].LeftPoint - p[j].DownPoint - BL;
    TR
    = p[j].LargeY - TL - p[j].UpPoint;
    BR
    = n - j - 1 - TR - p[j].RightPoint - p[j].UpPoint;

    if(TL + BR > Ollie_num)
    {
    Stan_num
    = BL + TR;
    Ollie_num
    = TL + BR;
    }
    else if(TL + BR == Ollie_num)
    {
    if(BL + TR <= Stan_num)
    {
    Stan_num
    = BL + TR;
    }
    }
    Updata(p[j].y);
    }
    V[num].Ollie
    = Ollie_num;
    V[num].Stan
    = Stan_num;
    i
    = j;
    }
    sort(V,V
    +num,cmp3);
    printf(
    "Stan: %d; Ollie:",V[0].Stan);
    for(i=0;i<num;i++)
    {
    if(V[i].Stan != V[0].Stan)
    break;
    if(V[i].Ollie == V[i-1].Ollie && i != 0)
    continue;
    printf(
    " %d",V[i].Ollie);
    }
    printf(
    ";\n");
    }
    //system("pause");
    return 0;
    }

     

  • 相关阅读:
    Zoj 2913 Bus Pass BFS
    Poj 1324 Holedox Moving 状压判重+BFS
    Poj 1465 Multiple BFS+余数判重
    HDU 1010 Tempter of the Bone dfs+剪枝
    Poj 1659 Frogs' Neighborhood 图的可图性判断
    ZOJ 3203 Light Bulb 三分
    HDU 1698 Just a Hook(线段树的区间修改)
    HDU 1698 Just a Hook(线段树的区间修改)
    Codeforces 1261B2 Optimal Subsequences (Hard Version)(树状数组)
    Codeforces 1261B2 Optimal Subsequences (Hard Version)(树状数组)
  • 原文地址:https://www.cnblogs.com/silencExplode/p/1876791.html
Copyright © 2011-2022 走看看