AcWing 241 楼兰图腾
题目传送门
树状数组主要解决的是
- 1、
a[x] += c(单点修改) - 2、求
a[L ~ R](前缀和)
#include
using namespace std;
const int N = 2000010;
typedef long long LL;
int n;
// t[i]表示树状数组i结点覆盖的范围和
int a[N], t[N];
// 可以理解为前缀和
// l1[i]表示左边比第i个位置小的数的个数
// g1[i]表示左边比第i个位置大的数的个数
int l1[N], g1[N];
// 可以理解为后缀和
// l2[i]表示右边比第i个位置小的数的个数
// g2[i]表示右边比第i个位置大的数的个数
int l2[N], g2[N];
//返回非负整数x在二进制表示下最低位1及其后面的0构成的数值
int lowbit(int x) {
return x & -x;
}
//将序列中第x个数加上k
void add(int x, int k) {
for (int i = x; i <= n; i += lowbit(i)) t[i] += k;
}
//查询序列前x个数的和
int sum(int x) {
int sum = 0;
for (int i = x; i; i -= lowbit(i)) sum += t[i];
return sum;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从左向右,依次统计每个位置左边比第i个数y小的数的个数、以及大的数的个数
for (int i = 1; i <= n; i++) {
int y = a[i];
// 1 ~ y-1之间有多少个数
l1[i] = sum(y - 1);
// y+1 ~ n有多少个数
g1[i] = sum(n) - sum(y);
//将y加入树状数组,即数字y出现1次
add(y, 1);
}
//清空树状数组,从右往左统计每个位置右边比第i个数y小的数的个数、以及大的数的个数
memset(t, 0, sizeof t);
//从右向左
for (int i = n; i >= 1; i--) {
int y = a[i];
l2[i] = sum(y - 1);
g2[i] = sum(n) - sum(y);
add(y, 1);
}
//用乘法原理统计结果
LL resA = 0, resV = 0;
for (int i = 1; i <= n; i++) {
resA += (LL)l1[i] * l2[i];
resV += (LL)g1[i] * g2[i];
}
//输出
printf("%lld %lld\n", resV, resA);
return 0;
}