题目大意
给出每个一组数代表1~n个学生的技能值,裁判在两名选手的中间,并且技能值也在两名选手之间。问一共能组织多少场比赛。
思路
对于一个学生i来说,假设他左边比他小的个数为ci那么比他大的为i-1-ci,在他右边比他小的个数为di那么比他大的个数为n-i-di
那么可以进行的比赛场次是
ci*(n-i-di) + (i-1-ci)*di
那么这个问题就变成求区间满足条件的值得个数。
可以用树状数组来求前缀和
令x[j]表示目前为止已经考虑的过的所有ai中是否存在一个ai=j(0表示没有,1表示有)则ci就是前缀和x[1] + x[2] + … + x[ai - 1]
同样也可以求di其反过来就行了。。。。
总结
没想到这样的统计方法,对于求小于某数的个数和来说这样求和很巧妙,当然只适合数据不大的情况下。。。。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxsz = (int)1e5 + 100;
const int maxn = (int)2e4 + 100;
ll v[maxn], c[maxn], d[maxn], b[maxsz];
ll n;
ll lowbit(ll x) {
return (x & -x);
}
ll sum(ll x) {
ll ret = 0;
while(x > 0) {
ret += b[x]; x -= lowbit(x);
}
return ret;
}
void Add(ll x, ll d) {
while(x < maxsz) {
b[x] += d; x += lowbit(x);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
//freopen("i.txt", "r", stdin);
int T; cin >> T;
while(T--) {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> v[i];
}
memset(b, 0, sizeof(b));
for(int i = 1; i <= n; i++) {
c[i] = sum(v[i]); // 计算前i个小于v[i]的个数
Add(v[i], 1);
}
// for(int i = 1; i <= n; i++) {
// cout << c[i] << " ";
// }
// cout << endl;
memset(b, 0, sizeof(b));
for(ll i = n; i >= 1; i--) {
d[i] = sum(v[i]); // 计算i往后小于v[i]的个数
Add(v[i], 1);
}
// for(int i = 1; i <= n; i++) {
// cout << d[i] << " ";
// }
// cout << endl;
ll ans = 0;
for(ll i = 1; i <= n; i++) {
ans += (i-1-c[i])*d[i] + c[i]*(n-i-d[i]);
}
cout << ans << endl;
}
return 0;
}