UVA1428 Ping pong(树状数组)

题目链接

 

题意:

n个人,从左到右排列,每个人都有一个不同的技能值 ai, 每场比赛3个人: 两名选手, 一名裁判。 裁判必须在两名选手中间, 并且技能值也在两名选手之间, 问一共能组织多少种比赛。

 

分析:

  考虑第i个人当裁判的情形。假设 ai 到 ai-1 中有 ci 个比 ai 个小, 那么就有 (i-1)-ci 个比 ai 大; 同理, 假设 ai+1 到 an 中有di个比ai 小, 那么就有 (n-i)-di 个比 ai 大。 根据乘法原理和加法原理, i当裁判 有 ci(n-i-di)+(i-ci-1)*di 种比赛。 这样问题转化为求 ci 和 di.

  题目已经明确每个人都有一个不同的技能值。 那么要求 ci , 先将树状数组 x 清空, 从左到右扫描 ai , 每运行一次 add(ai, 1), 就运行一次 sum(ai-1), sum求的是比 ai 小的数的个数, 因为是依次添加, 求 ai 时, ai 右边的数是没有加入树状数组 x 的, 这样 sum(ai-1), 求的就是 在 i 左边的比 ai 小的数的个数, 即 ci。 同理可以计算出 di

 

注意:

有一个调试了4个多小时的BUG(欲哭无泪啊), 那就是 数组 x 的大小并不是 n , 因为 n 代表人的个数,而 x 存的值为技能值,  即 x 的大小为 a1~an 最大的技能值。

 

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 100000 + 10;

int c[20000+10], d[20000+10], x[maxn], a[20000+10], n, mn;

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

void add(int p, int ad){
    while(p<=mn){   //这里是 mn, 并非 n
        x[p] += ad;
        p += lowbit(p);
    }
}

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

int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        mn = 0;

        scanf("%d", &n);

        for(int i=1; i<=n; i++){
            scanf("%d", &a[i]);
            mn = max(mn, a[i]);
        }

        memset(x, 0, sizeof(x));
        for(int i=1; i<=n; i++){
            add(a[i], 1);
            c[i] = sum(a[i]-1);
        }

        memset(x, 0, sizeof(x));
        for(int i=n; i>=1; i--){
            add(a[i], 1);
            d[i] = sum(a[i]-1);
        }

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

        printf("%lld\n", ans);
    }

    return 0;
}

 

 

posted on 2013-05-07 20:51  Still_Raining  阅读(211)  评论(0编辑  收藏  举报