【逆序对 权值树状数组】 楼兰图腾

传送门

题意

即给定 \(n\) 组坐标 \(\left(1, y_{1}\right),\left(2, y_{2}\right), \ldots,\left(n, y_{n}\right)\),其中 \(y_{1}\sim y_{n}\)\(1\sim n\) 的一组排列,
如果三个点 \(\left(i, y_{i}\right),\left(j, y_{j}\right),\left(k, y_{k}\right)\) 满足 \(1 \leq i<j<k \leq n\)\(y_{i}>y_{j}<y_{k}\),则称三点构成 \(V\) 型,
如果三个点 \(\left(i, y_{i}\right),\left(j, y_{j}\right),\left(k, y_{k}\right)\) 满足 \(1 \leq i<j<k \leq n\)\(y_{i}<y_{j}>y_{k}\), 则称这三个点构成 \(\wedge\) 图腾
求出给定的坐标中一共有多少个不同三元组的能构成 \(V\) 型和 \(\wedge\)

数据范围

\(n\leq 200000\)

题解

  • 维护两个数组,\(great,les\)\(great[i]\) 表示前 \(i\) 个数中有多少比第 \(i\) 个大的,\(les[i]\) 表示比第 \(i\) 小的
  • 构成 \(V\) 型和 \(\wedge\) 的是从\(i\) 个前选比其大或小的,其后选一个大或小的,即 \(\mathrm{C}_{n_{pre}}^{1} \times \mathrm{C}_{n_{aft}}^{1}\)
  • 第一次正序扫描,前缀和表示个数,维护每个 \(i\) 前比其大的和比其小的
  • 第二次倒序扫描,利用第一次的直接计算答案即可

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;

int c[N], a[N];
int pre_great[N], pre_small[N];
int n;

int lowbit(int x){
    return x & -x;
}
void add(int x, int y){
    for(int i = x; i <= n; i += lowbit(i)) {
        c[i] += y;
    }
}
int ask(int x){
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) {
        res += c[i];
    }
    return res;
}
int main(){
    cin>>n;
    for(int i = 1; i <= n; i ++) cin>>a[i];
 
    for(int i = 1; i <= n; i++){
        int now = a[i];
        pre_great[i] = ask(n) - ask(now);
        pre_small[i] = ask(now - 1);
        add(now, 1);
    }
    memset(c, 0, sizeof c);
    ll ans_v = 0, ans_r = 0;
    for(int i = n; i >= 1; i --){
        int now = a[i];
        ans_v += 1ll * pre_great[i] * (ask(n) - ask(now));
        ans_r += 1ll * pre_small[i] * ask(now - 1);
        add(now, 1);
    }
    cout << ans_v << ' ' << ans_r;
}

posted @ 2020-05-20 21:11  Hyx'  阅读(128)  评论(0)    收藏  举报