这题不是一眼题,值得做。
思路:
假设第个选手作为裁判,定义表示在裁判左边的中的能力值小于他的人数,表示裁判右边的中的能力值小于他的人数,那么可以组织场比赛。
那么现在考虑如何求得和数组。根据的定义知道我们要求的就是区间中能力值小于第i个人的能力值的数量,即能力值在区间的人数。我们定义表示能力值为i的人的数量。利用树状数组从左到右动态更新与维护很容易得到数组,同理得到数组。
注意:答案应该是longlong类型。
AC代码
//#define LOCAL
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long LL;
const int maxn = 20000+5;
int cnt[100000+5], Rank[maxn];
int left[maxn], right[maxn];
int maxRank;
int lowbit(int x) {
return x&-x;
}
void init() {
memset(cnt, 0, sizeof(cnt));
}
int getSum(int x) {
int res = 0;
while(x > 0) {
res += cnt[x];
x -= lowbit(x);
}
return res;
}
void update(int x, int d) {
while(x <= maxRank) {
cnt[x] += d;
x += lowbit(x);
}
}
void solve(int start, int end, int u, int *a) {
for(int i = start; i != end; i += u) {
a[i] = getSum(Rank[i]-1);
update(Rank[i], 1);
}
}
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif // LOCAL
int T, n;
scanf("%d", &T);
while(T--) {
init();
maxRank = -inf;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &Rank[i]);
maxRank = max(maxRank, Rank[i]);
}
solve(0, n, 1, left);
init();
solve(n-1, -1, -1, right);
LL ans = 0;
for(int i = 0; i < n; i++) { //枚举每一位裁判
ans += (LL)left[i] * (n-1-i-right[i]);
ans += (LL)right[i] * (i-left[i]);
}
printf("%lld\n", ans);
}
return 0;
}
如有不当之处欢迎指出!