【uva】1428 - Ping pong(树状数组)

周五需要检测 树状数组以及线段树,这几天猛练一下,晚上看了一下二叉索引树,感觉还可以理解,不过A题的还是很有难度的。

题意给的很明显了,给一个位置i,求这个位置左边和右边比它小的数的个数(求出小的,求大的用减就可以了,因为a[i]的值都不重复.

我们从左往右检查,我们用一个数组vis[i]记录i值是否出现过(出现过为1,没出现为0),那么遍历到a[i]的时候我们只需要求 vis[1] + vis[2] + ……+vis[a[i] - 1],这样每次我们都需要花大量时间进行查询,其次,加入你使用C[i] 代表 vis[1] + vis[2] + …… +vis[i],那么,每次更行a[i],你都需要更新C[j] ( j > a[i]),非常耗费时间。因此使用上了树状数组。

如果进行查询操作,求vis[1] + vis[2] + vis[x];

int Sum(int x){    /*求和的话向左边走*/
    int ans = 0;
    while(x > 0){
        ans += C[x];
        x -= lowbit(x);
    }
    return ans;
}

如果进行插入,该表vis的值

int Add(int x,int d){
    while(x < MAXD){
        C[x] += d;
        x += lowbit(x);
    }
}

此外,lowbit(x) = x & -x;

这题经过计算数据范围需要使用long long 表示

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define MAXD 100000 + 10
typedef long long LL;
int array[MAXD];
int C[MAXD];
int lowbit(int x){
    return x & -x;
}
int Sum(int x){    /*求和的话向左边走*/
    int ans = 0;
    while(x > 0){
        ans += C[x];
        x -= lowbit(x);
    }
    return ans;
}
int Add(int x,int d){
    while(x < MAXD){
        C[x] += d;
        x += lowbit(x);
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        int vis[MAXD]; /*记录i是否存在*/
        int c[MAXD],d[MAXD];
        memset(vis,0,sizeof(vis));
        memset(C,0,sizeof(C));
        scanf("%d",&n);
        for(int i = 1 ; i <= n ; i++)
            scanf("%d",&array[i]);
        for(int i = 1 ; i <= n ; i++){  /*左边比a[i]小的数个数*/
            int t = array[i];
            if(!vis[t]){
                vis[t] = 1;
                Add(t,1);
            }
            c[i] = Sum(t) - 1;
        }
        memset(C,0,sizeof(C));
        memset(vis,0,sizeof(vis));
        for(int i = n ; i >= 1; i--){  /*右边比a[i]小的数个数*/
            int t = array[i];
            if(!vis[t]){
                vis[t] = 1;
                Add(t,1);
            }
            d[i] = Sum(t) - 1;
        }
        LL ans = 0;
        for(int i = 1 ; i <= n ; i++){
            ans = ans + c[i] * (n - i - d[i]) + (i - 1 - c[i]) * d[i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值