zoukankan      html  css  js  c++  java
  • 树状数组模板+逆序对

    lowbit函数

    顾名思义,lowbit这个函数的功能就是求某一个数的二进制表示中最低的一位1,举个例子,x = 6,它的二进制为110,那么lowbit(x)就返回2,因为最后一位1表示2

    求lowbit的两种方法

    int lowbit(x) 
    {   
        return x - (x & (x - 1));
    }
    int lowbit(x) 
    {   
        return x & -x;
    }

    C[i]代表 子树的叶子结点的权值之和

    C[1]=A[1];
    C[2]=A[1]+A[2];
    C[3]=A[3];
    C[4]=A[1]+A[2]+A[3]+A[4];
    C[5]=A[5];
    C[6]=A[5]+A[6];
    C[7]=A[7];
    C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

    对照式子可以发现  C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k为i的二进制中从最低位到高位连续零的长度)例如i=8时,k=3;

    树状数组的代码

    输入 6
    1 2 3 4 5 6
    输出
    15
    1 3 3 10 5 11
    #include<iostream>
    #include <algorithm>
    #include <cstdlib>
    #include<cstdio>
    #include<cstring>
    #include <cmath>
    using namespace std;
    const int M=200000+10;
    const int MAX=0x3f3f3f3f;
    typedef long long ll;
    ll a[101010],c[101010],t[101010],n;
    int lowbit(ll x)
    {
        return x&(-x);
    }
    int Getsum(ll i)//求前几项和
    {
        ll ans=0;
        while(i>0)
        {
            ans+=c[i];
            i-=lowbit(i);
        }
        return ans;
    }
    void update(ll i,ll v)//更新
    {
        while(i<=n)
        {
            c[i]+=v;
            i+=lowbit(i);
        }
    }
    int main()
    {
        ll m,i,j;
        scanf("%lld",&n);
        for(i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            update(i,a[i]);//把c[i]求出来
        }
        ll ans=Getsum(5);//为前几项和
        printf("%lld
    ",ans);
        for(i=1;i<=n;i++)
        {
            printf("%lld ",c[i];
        }
        return 0;
    }

    若求前x到y项的和 Getsum(y)-Getsum(x-1)

    求逆序对

    首先看一下离散化

    离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
    通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。例如:
    原数据:1,999,100000,15;处理后:1,3,4,2;
    原数据:{100,200},{20,50000},{1,400};
    处理后:{3,4},{2,6},{1,5};

    举个例子     输入n=6    9 1 0 5 4 4  输出 5 2 1 4 3

    看离散化代码  (有去重的作用)

    #include<iostream>
    #include <algorithm>
    #include <cstdlib>
    #include<cstdio>
    #include<cstring>
    #include <cmath>
    using namespace std;
    const int M=200000+10;
    const int MAX=0x3f3f3f3f;
    typedef long long ll;
    ll a[101010],c[101010],t[101010];
    int main()
    {
        ll n,m,i,j;
        scanf("%lld",&n);
        for(i=1; i<=n; i++)
            cin>>a[i],t[i]=a[i];
        sort(t+1,t+n+1);
        m=unique(t+1,t+n+1)-t-1;
        for(i=1; i<=n; i++)
            a[i]=lower_bound(t+1,t+m+1,a[i])-t;
        for(i=1;i<=m;i++)
            printf("%lld
    ",a[i]);
        return 0;
    }
    

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序

    求逆序对的代码

    输入 5
    9 1 0 5 4
    输出 6
    #include<iostream>
    #include <algorithm>
    #include <cstdlib>
    #include<cstdio>
    #include<cstring>
    #include <cmath>
    using namespace std;
    const int M=200000+10;
    const int MAX=0x3f3f3f3f;
    typedef long long ll;
    ll a[101010],c[101010],t[101010],n;
    int lowbit(ll x)
    {
        return x&(-x);
    }
    int Getsum(ll i)//求前几项和
    {
        ll ans=0;
        while(i>0)
        {
            ans+=c[i];
            i-=lowbit(i);
        }
        return ans;
    }
    void update(ll i,ll v)//更新
    {
        while(i<=n)
        {
            c[i]+=v;
            i+=lowbit(i);
        }
    }
    int main()
    {
        ll m,i,j;
        scanf("%lld",&n);
        for(i=1; i<=n; i++)
         {
            scanf("%lld",&a[i]);
            t[i]=a[i];
            //update(i,a[i]);//把c[i]求出来
         }
        sort(t+1,t+n+1);
        m=unique(t+1,t+n+1)-t-1;
        for(i=1; i<=n; i++)
            a[i]=lower_bound(t+1,t+m+1,a[i])-t;
    //    for(int i=1;i<=m;i++)
    //        printf("%d
    ",a[i]);
        ll ans=0;
        for(i=1;i<=n;i++)
        {
             update(a[i],1);
             ans+=i-Getsum(a[i]);
        }
        printf("%lld
    ",ans);
        return 0;
    }

    区间a,b都加2 求区间x,y的和(区间修改 区间查询模板)

    #include<iostream>
    #include <algorithm>
    #include <cstdlib>
    #include<cstdio>
    #include<cstring>
    #include <cmath>
    using namespace std;
    const int M=200000+10;
    const int MAX=0x3f3f3f3f;
    typedef long long ll;
    ll a[101010],c[101010],t[101010],n=9;
    ll aa[10000]={0},bb[10000]={0};
    ll lowbit(ll x)
    {
        return x&(-x);
    }
    ll Getsum(ll i,ll q[])//求前几项和
    {
        ll ans=0;
        while(i>0)
        {
            ans+=q[i];
            i-=lowbit(i);
        }
        return ans;
    }
    void update(ll i,ll v,ll q[])//更新
    {
        while(i<=n)
        {
            q[i]+=v;
            i+=lowbit(i);
        }
    }
    int main()
    {
        //a,b区间都加2 求x,y区间的和 视最初都为0 所以只是求增加的和
        ll a,b,x,y,i,j,ans=0;
        memset(aa,0,sizeof(aa));
        memset(bb,0,sizeof(bb));
        scanf("%lld%lld%lld%lld",&a,&b,&x,&y);
        update(a,2,aa);
        update(b+1,-2,aa);
        update(a,a*2,bb);
        update(b+1,-(b+1)*2,bb);
        ans+=(y+1)*Getsum(y,aa)-Getsum(y,bb);
        ans-=x*Getsum((x-1),aa)
  • 相关阅读:
    Lambda
    Thread&线程池
    异常
    Map
    List and Set
    Collection和迭代器Iterator
    Object类,常用API
    (一)自定义 mybatis 之框架介绍
    Nginx三大功能及高并发分流
    http协议改为https
  • 原文地址:https://www.cnblogs.com/zcy19990813/p/9702726.html
Copyright © 2011-2022 走看看