• SCAU-1144 数星星-HDU-1166-树状数组的应用


    本文借鉴
    代码提供:https://www.cnblogs.com/geek1116/p/5566709.html
    树状数组详解:https://www.cnblogs.com/xenny/p/9739600.html
    不知道树状数组的同学呢就看看上面的链接啦讲的很棒呢(在学习树状数组的时候其实是可以同时学一下线段树的)
    在学习的时候可以看下这个题目,是可以用线段树来做也可以用树状数组来做的https://www.cnblogs.com/M-cag/archive/2012/08/16/2642459.html
    核心呢还是这个:2^k = i&(-i)

    C[i] = A[i - 2k+1] + A[i - 2k+2] + ... + A[i]

    如果是要修改了一个A[]的话会引起其他的c改变的,就要看那些的c[]包含了A[]
    A[i] 包含于 C[i + 2k]、C[(i + 2k) + 2k]...
    还是打一下基本的代码把(我也练下)
    下面用a[]来装原来的数组,然后用c[]来装树状数组的
    算2^k的函数是lowbit


    int lowbit(int x)//这里的x就是那个k了
    {
    return x&(-x);
    }

    如果是修改了A[]的话
    void update(int i,int k)//在i位置上加k也就是改变了A[]
    {
    while(i<=n)//只要还在范围内的话就可以一直的修改了
    {
    c[i]+=k;
    i+=lowbit(i);
    }
    }

    求和的话
    int getsum(int i)//求1到i的和
    {
    int res=0;
    while(i>0)
    {
    res+=c[i];
    i-=lowbit(i);
    }
    return res;
    }


    1144 数星星
    该题有题解

    时间限制:564MS 内存限制:65536K
    提交次数:193 通过次数:43

    题型: 编程题 语言: G++;GCC

     

    Description
    天文学家们喜欢观察星星。它们把每颗星星看成一个点,并把每颗星星左下方(即横坐标和纵坐标都不比它大)的星星颗数作为它的等级值。
    现给出所有星星(星星个数为N)的坐标,计算并输出指定编号的星星的等级。

    注意:不存在相同坐标的星星

     


    输入格式
    第一行为N
    后N行为编号为1到N的星星的坐标(坐标用整数)
    此后是M
    后一行是M个星星的编号

    N<=100000
    M<=1000

    坐标范围0<=x,y<=1000000

     

    输出格式
    要求依次输出这M个星星的等级,一行一个

     

     

    输入样例
    5
    0 0
    2 0
    3 0
    1 1
    2 2
    2
    4 5

     

     

    输出样例
    1
    3

    思路:一般这种输入x y坐标型的,或者一个物体是有两个属性的话,如果要进行比较的话,一帮都是先对其中一个变量进行排序,然后再吧问题变成了对另外一个变量的比较上了
    由于如果要对结构体进行比较的话最好还是用到c++来搞,所以下面代码是用c++来实现的了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <cctype>
    #include <queue>
    #include <stack>
    #include <map>
    #include <vector>
    #include <set>
    #include <utility>
    #define ll long long
    #define inf 0x3f3f3f3f
    #define MAX_N 100000
    #define MAX_X 1000000
    using namespace std;
    
    typedef struct node
    {
        int x,y;
        int p; //因为我们对数组进行了重新的排序,但是最好输入的是下标来看他的等级是多少的,所以为了之后可以输入下标得出结果,
         //就要再定义一个p来存放原来的下标值的
        //所以用个变量p来标记该元素的原下标
    } node;
    node star[MAX_N+5];//
    bool cmp(node a,node b) //按y大小升序来排序,y相同时把x较小的排前面
    {
        if(a.y!=b.y)
            return a.y<b.y;
        else
            return a.x<b.x;
    }
    int n,m,maxn;//n表示的是有多少个星星,然后m是要看多少个星星的等级,用maxn来存放星星里面横坐标最大的那个
    int ans[MAX_N+5]; //ans[]数组存储每个星星的等级,这个数组的下标表示的是输入数的下标,而并不是x或者y
    int bit[MAX_X+5]; //树状数组中的统计和的数组,也就是那个c数组了
    int sum(int pos)//就是板子了呢
    {
        int res=0;
        while(pos)
        {
            res+=bit[pos];//再次说bit[]就是板子里面的c[]
            pos-=(pos&-pos);
        }
        return res;
    }
    void updata(int pos,int value)//由于是以颗数来搞的,所以value其实就是1了,就是在相应的位置上加1的了
    {
        while(pos<=maxn)
        {
            bit[pos]+=value;//把只要和pos位置有关的数组都加1,其实就是输入了这个位置说明就有这个点了,所以和这个点有光的所以的bit[]就加1即可了
            pos+=(pos&-pos);
        }
    }
    int main()
    {
        //freopen("input.txt","r",stdin);
        memset(bit,0,sizeof(bit));//这里是没有用到另外一个数组a[]来装原数组的
        scanf("%d",&n);
        maxn=-1;
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d",&star[i].x,&star[i].y);
            star[i].x++;    //注意了:在树状下标不能有0,否则会死循环!
            star[i].y++;   //所以所有的横纵坐标都+1
            star[i].p=i;//就是下标,我们是从1开始到n的作为下标的
            if(star[i].x>maxn)
                maxn=star[i].x;  //更新最大的x坐标
        }
        sort(star+1,star+n+1,cmp);//以y坐标升序来sort
        //
        for(int i=1; i<=n; i++)
        {
            ans[star[i].p]=sum(star[i].x); //计算位于其左下方的星星个数,注意这个ans是以这个点的输入的次序也就是下标来的
            updata(star[i].x,1);  //更新bit[]数组
        }
        //
        scanf("%d",&m);
        while(m--)
        {
            int temp;
            scanf("%d",&temp);
            printf("%d
    ",ans[temp]);
        }
        return 0;
    }

    例题:

    敌兵布阵

     HDU - 1166

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int n,m;
     5 int a[50005],c[50005]; //对应原数组和树状数组
     6 
     7 int lowbit(int x){
     8     return x&(-x);
     9 }
    10 
    11 void updata(int i,int k){    //在i位置加上k
    12     while(i <= n){
    13         c[i] += k;
    14         i += lowbit(i);
    15     }
    16 }
    17 
    18 int getsum(int i){        //求A[1 - i]的和
    19     int res = 0;
    20     while(i > 0){
    21         res += c[i];
    22         i -= lowbit(i);
    23     }
    24     return res;
    25 }
    26 
    27 int main(){
    28     int t;
    29     cin>>t;
    30     for(int tot = 1; tot <= t; tot++){
    31         cout << "Case " << tot << ":" << endl;
    32         memset(a, 0, sizeof a);
    33         memset(c, 0, sizeof c);
    34         cin>>n;
    35         for(int i = 1; i <= n; i++){
    36             cin>>a[i];
    37             updata(i,a[i]);   //输入初值的时候,也相当于更新了值,所有就直接的调用update函数即可了
    38         }
    39 
    40         string s;//存放的是指令
    41         int x,y;
    42         while(cin>>s && s[0] != 'E'){//如果是E的话就结束了
    43             cin>>x>>y;//由于只要不是结束的话都是要输入两个数的 x和y的
    44             if(s[0] == 'Q'){    //求和操作
    45                 int sum = getsum(y) - getsum(x-1);    //x-y区间和也就等于1-y区间和减去1-(x-1)区间和
    46                 cout << sum << endl;
    47             }
    48             else if(s[0] == 'A'){
    49                 updata(x,y);//在x的位置上加y
    50             }
    51             else if(s[0] == 'S'){
    52                 updata(x,-y);    //减去操作,即为加上相反数
    53             }
    54         }
    55 
    56     }
    57     return 0;
    58 }
    View Code

    其他的扩展:
    区间更新、单点查询(差分建树)
    核心:当某个区间[x,y]值改变了,区间内的差值是不变的,只有D[x]和D[也就是说问题变成了:原来要更新一个区间的值变成了只需要更新两个点y+1]的值发生改变,
    所以我们就可以利用这个性质对D[]数组建立树状数组,
    也就是说问题变成了只用在原来的基础上吧第x个的加k,然后第y+1个的减k即可了

     1 int n,m;
     2 int a[50005] = {0},c[50005]; //对应原数组和树状数组
     3 
     4 int lowbit(int x){
     5     return x&(-x);
     6 }
     7 
     8 void updata(int i,int k){    //在i位置加上k
     9     while(i <= n){
    10         c[i] += k;
    11         i += lowbit(i);
    12     }
    13 }
    14 
    15 int getsum(int i){        //求D[1 - i]的和,即A[i]值
    16     int res = 0;
    17     while(i > 0){
    18         res += c[i];
    19         i -= lowbit(i);
    20     }
    21     return res;
    22 }
    23 
    24 int main(){
    25     cin>>n;27     for(int i = 1; i <= n; i++){
    26         cin>>a[i];
    27         updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
    28     }
    29     
    30     //[x,y]区间内加上k
    31     updata(x,k);    //A[x] - A[x-1]增加k
    32     updata(y+1,-k);        //A[y+1] - A[y]减少k
    33     
    34     //查询i位置的值
    35     int sum = getsum(i);
    36 
    37     return 0;
    38 }
    View Code

    区间更新、区间查询:还是利用了差分的思维了
    核心:维护两个数状数组,sum1[i] = D[i],sum2[i] = D[i]*(i-1);

     1 int n,m;
     2 int a[50005] = {0};
     3 int sum1[50005];    //(D[1] + D[2] + ... + D[n])
     4 int sum2[50005];    //(1*D[1] + 2*D[2] + ... + n*D[n])
     5 
     6 int lowbit(int x){
     7     return x&(-x);
     8 }
     9 
    10 void updata(int i,int k){
    11     int x = i;    //因为x不变,所以得先保存i值
    12     while(i <= n){
    13         sum1[i] += k;
    14         sum2[i] += k * (x-1);
    15         i += lowbit(i);
    16     }
    17 }
    18 
    19 int getsum(int i){        //求前缀和
    20     int res = 0, x = i;
    21     while(i > 0){
    22         res += x * sum1[i] - sum2[i];
    23         i -= lowbit(i);
    24     }
    25     return res;
    26 }
    27 
    28 int main(){
    29     cin>>n;
    30     for(int i = 1; i <= n; i++){
    31         cin>>a[i];
    32         updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值
    33     }
    34 
    35     //[x,y]区间内加上k
    36     updata(x,k);    //A[x] - A[x-1]增加k
    37     updata(y+1,-k);        //A[y+1] - A[y]减少k
    38 
    39     //求[x,y]区间和
    40     int sum = getsum(y) - getsum(x-1);
    41 
    42     return 0;
    43 }
    View Code

    区间修改、单点查询模板题目:https://www.luogu.org/problem/show?pid=3368

    区间修改、区间查询模板题目:https://vjudge.net/problem/POJ-3468
    最后再打波广告:https://www.cnblogs.com/xenny/p/9739600.html讲的实在是太好了

  • 相关阅读:
    前端经典书籍
    D3基本概念
    Array.map和parseInt的用法
    首屏和白屏时间计算
    css换肤总结
    文件上传总结
    js的uuid
    html5 drag事件用法
    shell脚本中的逻辑判断 文件目录属性判断 if特殊用法 case判断
    Mac vim“装逼”配置
  • 原文地址:https://www.cnblogs.com/SCAU-gogocj/p/11938997.html
Copyright © 2020-2023  润新知