题目链接: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]∗(n−i−d[i])+(i−c[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]。