UVALive 4329 Ping pong(树状数组求逆序数+顺序数)

来源:互联网 发布:数据错误 循环冗余检查 编辑:程序博客网 时间:2024/06/08 14:00

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2330

题意:对于给定的一个长度为n序列a,对于每个位置i,若左边存在一个数a[l],右边存在一个数a[r],满足a[l] < a[i] < a[r]或a[l] > a[i] > a[r],则(l, r)构成一对,求总共有多少对。

思路:虽然先学的是线段树,但是这类问题也可以用树状数组解决。考虑2个数组c和d,对于位置i,c[i]表示位置i左边有c[i]个数比它小,d[i]表示位置i右边有d[i]个数比它小。则最后的结果就是

i=1nc[i](nid[i])+(i1c[i])d[i]

代码:

#include <stdio.h>#include <iostream>#include <string.h>#include <math.h>using namespace std;const int N = 2e4 + 10;const int M = 1e5 + 10;int s[M << 2];int c[M], d[M];int a[N];int n;int lowbit(int x) {    return x & (-x);}void add(int x) {    while (x <= M) {        s[x]++;        x += lowbit(x);    }}int sum(int x) {    int res = 0;    while (x > 0) {        res += s[x];        x -= lowbit(x);    }    return res;}int main() {        int t_case;    scanf("%d", &t_case);    for (int i_case = 1; i_case <= t_case; i_case++) {        scanf("%d", &n);        for (int i = 1; i <= n; i++)            scanf("%d", &a[i]);        memset(s, 0, sizeof(s));        for (int i = 1; i <= n; i++) {            c[i] = sum(a[i] - 1);            add(a[i]);        }        memset(s, 0, sizeof(s));        for (int i = n; i >= 1; i--) {            d[i] = sum(a[i] - 1);            add(a[i]);        }        long long res = 0;        for (int i = 2; i < n; i++)            res += 1LL * c[i] * (n - i - d[i]) + 1LL * (i - 1 - c[i]) * d[i];        printf("%lld\n", res);    }    return 0;}
0 0
原创粉丝点击