poj 3928 Ping pong(数据结构:树状数组)

这篇博客讲述了作者在解决POJ 3928问题时,如何运用树状数组(也称线段树)解决关于数据结构的难题。作者回顾了自己初次接触该问题时的困扰,以及现在对该问题的理解和解决方案,尽管在实现过程中遇到了一些错误。代码实现被提及,但未给出详细内容。

白书上树状数组例题,感觉看起来太吃力了

设输入为pos[i]

则我们令

c[i]为1-(i-1)这些人中,rank小于pos[i]的人数个数

d[i]为(i+1)到n这些人中,rank小于pos[i]的人数个数

易知答案为所有c[i]*(n-i-d[i])+(i-1-c[i])*d[i]的总和

构造树状数组a[i],代表rank值小于i的人的个数

求解如下:

#include <stdio.h>
#include <string.h>
#define MAXN 100100
#define LL long long
int a[MAXN], c[MAXN];
int pos[MAXN];

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

int sum(int x) {
    int ans = 0;
    while(x > 0) {
        ans += a[x];
        x -= lowbit(x);
    }
    return ans;
}

int add(int x, int d) {
    while(x <= MAXN) {
        a[x] += d;
        x += lowbit(x);
    }
}

int main(void) {
    int i, T, N;
    LL ts, ans;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &N);
        ans = 0;
        memset(a, 0, sizeof(a));

        for(i=1; i<=N; ++i) {
            scanf("%d", &pos[i]);
            add(pos[i], 1);
            //更新rank值大于pos[i]的人的个数
            c[i] = sum(pos[i]-1);
            //统计rank值在1到pos[i]之间人的个数,由循环可知,此时保证在1-(i-1)pos内求解
//            printf("c[%d] = %d\n", i, c[i]);
        }
        for(i=1; i<=N; ++i) {
            ts = sum(pos[i]-1);
            //统计rank值在11到pos[i]之间人的个数,由循环可知,此时保证在1-n pos内求解,故减去c[i]即为i+1到n pos中rank小于pos[i]的个数
//            printf("d[%d] = %d\n", i, ts-c[i]);
            ans += c[i]*(N-i-(ts-c[i]));
            ans += (i-1-c[i])*(ts-c[i]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

太逗比了,刚刚又写了一遍,记得暑假的时候看这个题特别吃力

现在感觉好容易的一道题啊...明明是线段树练手题,这次写了一个bug。。。狂wa

代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 120010
#define LL long long
using namespace std;

int a[MAXN+10];
LL c[MAXN+10], d[MAXN+10], x[MAXN+10];

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

int sum(int k) {
    int tmp = k;
    LL ret = 0;
    while(k > 0) {
        ret += x[k];
        k -= lowbit(k);
    }
    return ret;
}

void add(int k, int d) {
    while(k <= MAXN) {
       x[k] += d; 
       k += lowbit(k);
    }
}

int main(void) {
    int T, n;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        memset(x, 0, sizeof(x));
        for(int i=1; i<=n; ++i) {
            scanf("%d", &a[i]);
            add(a[i], 1);
            c[i] = sum(a[i])-1;
            //printf("c[%d] = %d\n", i, c[i]);
        }
    
        memset(x, 0, sizeof(x));
        for(int i=n; i>0; --i) {
            add(a[i], 1);
            d[i] = sum(a[i])-1;
            //printf("d[%d] = %d\n", i, d[i]);
        }

        LL ans = 0ll;
        for(int i=1; i<=n; ++i) {
            ans += c[i]*(n-i-d[i])+(i-1-c[i])*d[i];
        }

        cout << ans << endl;

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值