树状数组求逆序对是树状数组的一个基本的运用.我们假设当前要求序列a的逆序对
1.在序列a的数值范围上建立树状数组,初始化为全0
2.倒序扫描序列a,对于每个数a[i]:
(1)在树状数组中查询前缀和[1, a[i] - 1],累加到答案ans中.(注意,查询前缀和时,查询的应该是[l - 1, r],而不是[l, r], 比如在这里我们要查询的其实是[0, a[i] - 1])
(2)执行"单点增加"操作,把位置上的数加1(相当于c[a[i]]++),同时正确维护c的前缀和.这表示数值a[i]又出现了一次.
3.最后得到ans
这道题我们可以用树状数组来求逆序对数
代码如下(详细讲解请见注释):
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a[200010], c[200010];
int l[200010], r[200010];
int n;
int ask(int x){
int ans = 0;
for (; x; x -= x & -x)
ans += c[x];
return ans;
}
void add(int x, int y){
for (; x <= n; x += x & -x)
c[x] += y;
}
signed main(){
scanf("%lld", &n);
for (int i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
int ans = 0;
//求v图腾:
//1.正序遍历a[i],统计前面比a[i]大的数的个数l[i]
//2.倒序遍历a[i],统计后面比a[i]大的数的个数r[i]
for (int i = 1; i <= n; ++i){
l[i] = ask(n) - ask(a[i]);
add(a[i], 1);
}
memset(c, 0, sizeof c);
for (int i = n; i; --i){
r[i] = ask(n) - ask(a[i]);
add(a[i], 1);
}
for (int i = 1; i <= n; ++i)
ans += l[i] * r[i];
printf("%lld ", ans);
memset(r, 0, sizeof r);
memset(l, 0, sizeof l);
memset(c, 0, sizeof c);
ans = 0;
//求^图腾:
//1.正序遍历a[i],统计前面比a[i]小的数的个数l[i]
//2.倒序遍历a[i],统计后面比a[i]小的数的个数r[i]
for (int i = 1; i <= n; ++i){
l[i] = ask(a[i] - 1);
add(a[i], 1);
}
memset(c, 0, sizeof c);
for (int i = n; i; --i){
r[i] = ask(a[i] - 1);
add(a[i], 1);
}
for (int i = 1; i <= n; ++i)
ans += l[i] * r[i];
printf("%lld ", ans);
}