uvalive 4329 Ping Pong(树状数组)

题目链接:https://vjudge.net/problem/UVALive-4329

题意:

一条直线上有n个乒乓选手,每个人有一个技能值ai,他们之间需要切磋,需要一个裁判,裁判的技能值要在两个人之间,问一共有多少个个方案。裁判不同或者选手不同都可以看做是不同的方案。

思路:

参照白书。考虑第i个人当裁判,设a[1]到a[i-1]有c[i]个比a[i]小,那么有(i-1)-c[i]个比a[i]大,a[i+1]到a[i]有d[i]个比a[i]小,那么有(n-i)-d[i]个比a[i]大,那么得到公式

ans=c[i](nid[i])+(ic[i]1)d[i]

做法:

使用线段树或者树状数组。
使用树状数组时:
求b[](此处的b[]相当于上文的c[]),n次add,将大于a[i]的点加上1,每次求下标i左边小于a[i]的个数,用b[i]来记录。
求d[],倒着枚举(因为求的是在i的右边小于a[i]的个数,如果正着枚举,到i时,树状数组里已经记录了前i个中比a[i]小的个数,这是不对的),其他的和求b[]同理。
最后O(n)求和就可以得到答案。

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int maxn = 100000 + 100;
int t, n, a[maxn], c[maxn], b[maxn], d[maxn];

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

void add(int x){
    while(x<=maxn){
        c[x] += 1;
        x += lowbit(x);
    }
}

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

int main(){ 
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1; i<=n; ++i) cin>>a[i];
        memset(c, 0, sizeof(c));
        for(int i=1; i<=n; ++i){
            add(a[i]);
            b[i] = sum(a[i]-1);
        }
        memset(c, 0, sizeof(c));
        for(int i=n; i>0; --i){
            add(a[i]);
            d[i] = sum(a[i]-1);
        }
        long long ans = 0;
        for(int i=1; i<n; ++i){
            ans += (long long)b[i]*(n-i-d[i]) + (long long)(i-b[i]-1)*d[i]; 
        }
        cout<<ans<<endl;
    }
    return 0;
}

使用线段树:
按照能力值建树,即0到100000,每次更新把区间[a[i], 100000]加一,正着枚举a[i],可以得到c[],同理重新建树,逆着枚举a[i]得到d[i]。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值