P10589 题解

经典题。tag:数状数组


开一个权值树状数组,从左往右遍历,统计左边比 \(y_i\) 小的数字个数 \(ul_i\) 与比 \(a_i\) 大的数字个数 \(dl_i\);然后从右往左遍历,统计右边比 \(y_i\) 小的数字个数 \(dr_i\) 与比 \(a_i\) 大的数字个数 \(ur_i\)

两个答案即为 \(\sum_{i=1}^n dl_i \cdot ur_i\)\(\sum_{i=1}^n ul_i \cdot dr_i\)

当然用线段树也可以。树状数组优势在于代码很好写。

#include <bits/stdc++.h>
using namespace std;

#define ll long long
const int N = 2e5+10;
int n; ll c[N], a[N], ul[N], dl[N], ur[N], dr[N];
int lbt(int x) {return x & -x;}
void add(int k, ll x) {for (int i = k; i <= n; i += lbt(i)) c[i] += x;}
ll qry(int k) {ll rs = 0; for (int i = k; i; i -= lbt(i)) rs += c[i]; return rs;}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for (int i = 1; i <= n; i++) {
        if(i > 1) dl[i] = qry(n) - qry(a[i]), ul[i] = qry(a[i] - 1);
        add(a[i], 1);
    }
    memset(c, 0, sizeof c);
    for (int i = n; i; i--) {
        if(i < n) ur[i] = qry(n) - qry(a[i]), dr[i] = qry(a[i] - 1);
        add(a[i], 1);
    }
    ll ans1 = 0, ans2 = 0;
    for (int i = 1; i <= n; i++) ans1 += dl[i] * ur[i], ans2 += ul[i] * dr[i];
    printf("%lld %lld", ans1, ans2);
    return 0;
}
posted @ 2024-07-04 10:58  White_Way  阅读(42)  评论(0)    收藏  举报