UVALive ~ 4329 ~ Ping pong (树状数组)

题意:一条大街上任着n个乒丘球爱好者,经常组织比赛切磋技术。每个人都有一个不同技能值a[i],每场比赛需要3个人: 两名选手,一名裁判。他们有一个奇怪的规定,即裁判必须住在两名选手的中间,并且技能值也在两名选手之间。问一共能组织多少种比赛。
【输入格式】
输入第一行为数据组数T(1<T<20)。每组数据占一行,首先是整数n(3<=n<=20000)然后是个n不同的数即a[i]...按照住所从左到右的顺序给出每个乒乓爱好者的技能值。
【输出格式】
对于每组数据,输出比赛总数的值。
【分析】

考虑第i个人当裁判的情形。假设a[i]到a[n] 中有L[i]个比a小,那么就有(i-1)-L[i]个比a[i]大,同理假设a[i+1]到a[n]中有R[i]个比a[i]小,那么就有(n-i)-R[i]个比a[i]。根据乘法原理和加法原理,i当裁判有L[i]*(n-i-R[i]) + (i-L[i]-1)*R[i]种比赛。这样问题就转化为求L[i]和d[i]了。

L[i]可以这样计算:从左到右扫描所有的a[i],x[j]表示到目前为止已经考虑过得a[i]中,是否存在一个a[i]=j(x[j]=0表示j不存在,1表示存在),则C[i]就是前缀和x[1]+x[2]+...x[a-1]。初始时所有x[i]=0,在计算C[i]时,需要先设x[a[i]]=1,然后求前缀和。换句话说,我们需要动态的修改单个元素值并求其中你会和———这正是BIT的标准用法。这样,就可以在O(nlogr)(这里的r是a[i]的上限)的时间内计算出所有L[i]和R[i],然后在O(n)时间里累加出答案。

以上内容来自算法竞赛入门经典


注意本题中树状数组的大小是a[i]的上限不是个数N。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int n, a[MAXN], C[MAXN], l[MAXN], r[MAXN];
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;
}
void add(int x, int d)
{
    while (x < MAXN)
    {
        C[x] += d;
        x += lowbit(x);
    }
}
int main()
{
    int T; scanf("%d", &T);
    while (T--)
    {
        memset(C, 0, sizeof(C));
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            l[i] = sum(a[i]-1);
            add(a[i], 1);
        }
        memset(C, 0, sizeof(C));
        for (int i = n; i >= 1; i--)
        {
            r[i] = sum(a[i]-1);
            add(a[i], 1);
        }
        long long ans = 0;
        for (int i = 2; i < n; i++)
        {
            ans += l[i]*(n-i-r[i]) + (i-l[i]-1)*r[i];
        }
        printf("%lld\n", ans);
    }
    return 0;
}
/*
1
3 1 2 3
*/



猜你喜欢

转载自blog.csdn.net/zscdst/article/details/80190567