离散化+树状数组

树状数组log(N)的良好性能广泛适用于大数据的区间和修改,例如代码仓库查询

通过构建A[i]和C[i]对数据进行树状修改

A[i]:储存数据

C[i]:存放了A[0]~A[i]的和,C[x]母节点为C[x+lowbit(x)]

#define lowbit(x) (x&(-x))

单点修改&&更新后缀和:

void add(int x,int k)
{
    for (; x <= 200; x += lowbit(x)) c[x]+=k;
}

查询前缀和:

int ask(int x)
{
    int s = 0;
    for (; x > 0; x -= lowbit(x)) s += c[x];
    return s;
}

例:求逆序数

利用桶的思想,每读入一个元素x查询0~x的桶有几个非空,利用传统数组复杂度为(n*n),所以可以利用树状数组,当数据小时,可以单纯的利用桶的思想和树状数组

#include "bits/stdc++.h"
using namespace std;
#define N 100
#define lowbit(x) (x&(-x))
int8_t c[N] = {0};
void add(int x, int k) {
    for (; x <= N; x += lowbit(x))
        c[x] += k;
}
int ask(int x) {
    int s = 0;
    for (; x > 0; x -= lowbit(x))
        s += c[x];
    return s;
}
int main() {
    int x,n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &x);
        if (i == 0)
            printf("%d", ask(x));//每次输入一个值,计算他之前的和
        else
            printf(" %d", ask(x));
        add(x + 1, 1);//放入桶
    }
    printf("\n");
}

离散化

通过离散化能将大数化小,减少时空占用,例如:

原数组:5 , 9e3 , 3e7 , -10

离散化:2,3,4,1

#include "bits/stdc++.h"
using namespace std;
#define N 1000000
int a[N],b[N];
int main()
{
    int m;
  scanf("%d",&m);
        for(int i=0; i<m; i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(a,a+m);
        int cnt=unique(a,a+m)-a;   //离散化后的长度(去重)
        for(int i=0; i<m; i++) {
            b[i] = lower_bound(a, a + cnt, b[i]) - a + 1; //b[i]离散化后对应的值
            printf("%d ",b[i]);
        }
    printf("%d",cnt);

}

离散化+树状数组解决逆序数

#include "bits/stdc++.h"

using namespace std;
#define N 500005
#define lowbit(x) (x&(-x))
long long c[N] = {0};

void lisan(int *a, int *b, int a_len) {
    for (int i = 0; i < a_len; i++) {
        b[i] = a[i];
    }
    sort(a, a + a_len);
    int cnt = unique(a, a + a_len) - a;   //离散化后的长度(去重)
    for (int i = 0; i < a_len; i++) {
        b[i] = lower_bound(a, a + cnt, b[i]) - a + 1; //b[i]离散化后对应的值
    }

}

void add(int x, int k) {
    for (; x <= N; x += lowbit(x))
        c[x] += k;
}

int ask(int x) {
    int s = 0;
    for (; x > 0; x -= lowbit(x))
        s += c[x];
    return s;
}

int main() {
    int a[N], b[N], m;
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &a[i]);
    }
    lisan(a, b, m);
    int x;
    long long sum = 0;
    for (int i = 0; i < m; i++) {
        x = b[i];
        sum += i - ask(x);
        add(x, 1);//放入桶
    }
    printf("%lld\n", sum);

}

树状数组进阶

将C[I]定义为A[I]-A[I-1]那么想要修改一个区间内的值,复杂度降为logn而非nlogn

#include "bits/stdc++.h"
using namespace std;
#define N 500010
#define lowbit(x) (x&(-x))
long long c1[N] = {0},c2[N]={0};
void lisan(long long *a, long long *b, long long a_len) {
    for (long long i = 0; i < a_len; i++) {
        b[i] = a[i];
    }
    sort(a, a + a_len);
    long long cnt = unique(a, a + a_len) - a;   //离散化后的长度(去重)
    for (long long i = 0; i < a_len; i++) {
        b[i] = lower_bound(a, a + cnt, b[i]) - a + 1; //b[i]离散化后对应的值
    }

}




void add(long long x, long long  k) {  //差分更新
    for (long long i=x; i <= N; i += lowbit(i)) {
        c1[i]+= k;
        c2[i]+=k*(x-1);
    }
}
void add_point(long long x,long long k)//单点更新
{
    add(x,k);
    add(x+1,-k);
}
void add_block(long long  a,long long b,long long k) {//区间更新
    add(a,k);
    add(b+1,-k);
}

long long ask(long long x) {  //区间查询
    long long s = 0;
    for (long long i=x; i > 0; i -= lowbit(i))
    {s+= x*c1[i]-c2[i];}
    return s;
}

int main() {
    long long n, m;
    scanf("%lld%lld", &n,&m);
    long long sum = 0;

    long long  a[n+1]={0};
    for (long long i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        add(i, a[i]-a[i-1]);
    }
    for(long long i=0;i<n;i++)
    {
        long long d;
        scanf("%lld",&d);
        if(d==1)
        {
            long long  c;
            long long a,b;
            scanf("%lld%lld%lld",&a,&b,&c);
            add_block(a,b,c);
        }
        else
        {
            long long a;
            scanf("%lld",&a);
            printf("%lld\n",ask(a)-ask(a-1));
        }
    }




    /* long long a[n]={0},b[n]={0};
     for(long long i=0;i<n;i++)
     {
         scanf("%lld",&a[i]);
     }
 lisan(a,b,n);
     for (long long i =0; i<n; i++) {
         long long x=b[i];
         sum += i - ask(x+1);
         add_polong long(x+1,1);
     }
     prlong longf("%lld\n",sum);
 */
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LoseHu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值