时隔好久,终于隐隐约约搞懂了树状数组。
树状数组原来就不大会用,这道题也是似是而非。终于懂了就写写吧。
题目大意:
一条大街上住着n个乒乓球爱好者,他们经常组织乒乓球比赛且每个人的能力值ai都不同.每次比赛需要2个比赛者和一个裁判,他们有一个奇怪的规定:当裁判的那个人必须住在这两个比赛者之间,且裁判的能力值也必须在这两个人之间.问一共有多少种比赛组织方式.
输入:首先是T(1<=T<=20),表示实例个数.对于每个实例第一行是一个n(3<=n<=20000),然后是n个不同的整数即a1,a2…an(1<=ai<=100000),按照他们的住所位置从左到右给出每个人的能力值.
输出:比赛组织的总数.
思路:假设位置i,在[1,i-1]区间,比i这个人能力小的有ss个,那么这个区间比i能力大的人有(i-1)-ss个;
在【i+1,n】区间,比i这个人能力小的有ff个,那么在这个区间比i能力大的人有(n-i)-ff个
那么假设i是裁判,能裁定的方式有ss*((n-i)-ff)+((i-1)-ss)*ff个
然后所有(1~n)加和即可。
**难点在于,如何快速的找出比这个人能力小的人的个数来。
这时候就要想树状数组的真实含义了。
剖析树状数组:
树状数组不过就是巧妙地利用一个数组c[i],还有两个函数,add和sum
如图,它的意思就是每次最小的更改几个c变化要变化的c。
这道题呢,要把下标1~n改为改为a[i]能力值,为什么可以这样呢,因为树状数组在求和时的sum函数是返回的a【1】~a【n】的和,我们用能力值作为下标,如果这个数存在就+1,标记有这个数。那么标记前求和一次,返回的就是比他小的个数的数量,因为一开始输入的能力值虽然不是有序的,但是在树状数组内求和的时候是有序的(从小到大),因为能力值大的在后边。举个例子,输入的能力值为4 5 3 6,那么第一个数的时候执行一次求和,显然这个时候5 3 6都还没有标记,所以为0.然后标记C【4】++(用能力值作为下标),然后到5,先执行一次求和,显然答案就为1,之后C【5】++,到了3,先求一次和,因为树状数组返回的是c[1]+c[2]+c[3],那么显然答案为0,然后C【3】++,到了6,执行求和,答案显然为3,然后标记。。。。这个问题就完美解决了。。。。
正着找一边,然后c数组清空,再反着找一遍。
代码如下:
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 20000+10
#define maxnn 100000+10
#define LL long long
int cas=1,T;
int a[maxn];
int L[maxn];
int R[maxn];
int c[maxnn];
int maxx;
int lowbit(int i)
{
return i&(-i);
}
int sum(int i)
{
int ans = 0;
while (i)
{
ans +=c[i];
i-=lowbit(i);
}
return ans;
}
void add(int i,int d)
{
while (i<=maxx)
{
c[i]+=d;
i+=lowbit(i);
}
}
int main()
{
//freopen("in","r",stdin);
scanf("%d",&T);
while (T--)
{
int n;
scanf("%d",&n);
memset(a,0,sizeof(a));
maxx=0;
for (int i = 1;i<=n;i++)
{
scanf("%d",&a[i]);
maxx = max(maxx,a[i]);
}
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
memset(c,0,sizeof(c));
for (int i = 1;i<=n;i++)
{
L[i] = sum(a[i]);
add(a[i],1);
}
memset(c,0,sizeof(c));
for (int i = n ;i>=1;i--)
{
R[i] = sum(a[i]);
add(a[i],1);
}
LL ans = 0;
for (int i = 1;i<=n;i++)
{
ans += ( (LL)(L[i]*(n-i-R[i])) + (LL)(R[i]*(i-L[i]-1)) );
}
printf("%lld\n",ans);
}
//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
return 0;
}