【ACWing】241. 楼兰图腾

题目地址:

https://www.acwing.com/problem/content/243/

在完成了分配任务之后,西部 314 314 314来到了楼兰古城的西部。相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀 ( V ) (V) (V),一个部落崇拜铁锹 ( ∧ ) (∧) (),他们分别用 V V V ∧ ∧ 的形状来代表各自部落的图腾。西部 314 314 314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n n n个点,经测量发现这 n n n个点的水平位置和竖直位置是两两不同的。西部 314 314 314认为这幅壁画所包含的信息与这 n n n个点的相对位置有关,因此不妨设坐标分别为 ( 1 , y 1 ) , ( 2 , y 2 ) , … , ( n , y n ) (1,y_1),(2,y_2),…,(n,y_n) (1,y1),(2,y2),,(n,yn),其中 y 1 ∼ y n y_1∼y_n y1yn 1 1 1 n n n的一个排列。西部 314 314 314打算研究这幅壁画中包含着多少个图腾。如果三个点 ( i , y i ) , ( j , y j ) , ( k , y k ) (i,y_i),(j,y_j),(k,y_k) (i,yi),(j,yj),(k,yk)满足 1 ≤ i < j < k ≤ n 1≤i<j<k≤n 1i<j<kn y i > y j , y j < y k y_i>y_j,y_j<y_k yi>yj,yj<yk,则称这三个点构成 V V V图腾;如果三个点 ( i , y i ) , ( j , y j ) , ( k , y k ) (i,y_i),(j,y_j),(k,y_k) (i,yi),(j,yj),(k,yk)满足 1 ≤ i < j < k ≤ n 1≤i<j<k≤n 1i<j<kn y i < y j , y j > y k y_i<y_j,y_j>y_k yi<yj,yj>yk,则称这三个点构成 ∧ ∧ 图腾;西部 314 314 314想知道,这 n n n个点中两个部落图腾的数目。因此,你需要编写一个程序来求出 V V V的个数和 ∧ ∧ 的个数。

输入格式:
第一行一个数 n n n
第二行是 n n n个数,分别代表 y 1 , y 2 , … , y n y_1,y_2,…,y_n y1y2,,yn

输出格式:
两个数,中间用空格隔开,依次为 V V V的个数和 ∧ ∧ 的个数。

数据范围:
对于所有数据, n ≤ 200000 n≤200000 n200000,且输出答案不会超过int64
y 1 ∼ y n y_1∼y_n y1yn 1 1 1 n n n的一个排列。

可以用树状数组来做,树状数组可以参考https://blog.csdn.net/qq_46105170/article/details/103870987。例如我们考虑求 y i y_i yi左边比其小的数的个数,考虑一个长 n n n的数组 A A A,一开始全是 0 0 0,然后按从左到右次序将 A [ y j ] , 1 ≤ j ≤ i − 1 A[y_j],1\le j\le i-1 A[yj],1ji1依次加 1 1 1,这样求 A A A的前 y i − 1 y_i-1 yi1个数的前缀和即知 y i y_i yi左边比其小的数的个数。类似办法可以求 y i y_i yi右边比其小的数,然后利用乘法原理即可求出 ∧ \wedge 型的图案个数。 ∨ \lor 型图案个数类似。单点修改与前缀和,可以利用树状数组来做。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 2e5 + 10;
int n, a[N];
int tr[N];
int larger[N], smaller[N];

int lowbit(int x) {
    
    
    return x & -x;
}

void add(int k, int c) {
    
    
    for (int i = k; i <= n; i += lowbit(i)) tr[i] += c;
}

int sum(int k) {
    
    
    int res = 0;
    for (int i = k; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main() {
    
    
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i++) {
    
    
        larger[i] = sum(n) - sum(a[i]);
        smaller[i] = sum(a[i] - 1);
        add(a[i], 1);
    }

    memset(tr, 0, sizeof tr);
    long res1 = 0, res2 = 0;
    for (int i = n; i; i--) {
    
    
        res1 += larger[i] * (long) (sum(n) - sum(a[i]));
        res2 += smaller[i] * (long) (sum(a[i] - 1));
        add(a[i], 1);
    }

    printf("%ld %ld\n", res1, res2);

    return 0;
}

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)

おすすめ

転載: blog.csdn.net/qq_46105170/article/details/121471896
241