最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

AcWing241. 楼兰图腾

来源:博客园


(相关资料图)

传送门

题目大意

\(\qquad\)\(1.\)给你一个序列,让你统计三个数\(i,j,k\)(\(i a_j且a_ja_k\)的\(i,j,k\)对数

解题思路

\(\qquad\)我们已经知道如何利用树状数组求出一个数的后面有多少个数比它小。所以我们可以采取这种算法:\(\qquad\)\(1.\)对于^字符,我们需要的其实是:\(\qquad\)\(\qquad\)\(a.\)找出每个数i的前面比它小的字符数,记作l[i]\(\qquad\)\(\qquad\)\(b.\)找出每个数i后面比它大的字符数,记作r[i]\(\qquad\)\(\qquad\)然后根据\(\color{#ff003f}{乘法原理}\),当一个数\(num\)被当做分界点\(j\)的时候,此时第一个数\(i\)的选法有l[num]种(只要比\(num\)小且在\(num\)的前面就行),第三个数\(k\)的选法有r[num]种(只要比\(num\)大而且在\(num\)的后面就行),所以以\(num\)为分界点时,方案有\(l[num] \times r[num]\),而在一个序列中,每个数都可以作为分界点,所以枚举每个数,用resA统计\(\qquad\)\(2.\)同样地对于字符V,我们也这样统计。\(\\\)

解题代码

这里代码放两份,一份是规矩扫描,另一份是边扫描第二次边统计。

代码1
#include #include #include using namespace std;using LL = long long;const int N = 2e5 + 10;int n, a[N], Al[N], Bl[N], tr[N], Ar[N], Br[N];void add(int x, int v) {    for (; x <= n; x += x & -x) tr[x] += v;}int ask(int x) {    int res = 0;    for (; x; x -= x & -x) res += tr[x];    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 ++ )     {        int j = a[i];        Al[j] += ask(j - 1), Ar[j] += ask(n) - ask(j); //Al是一个数前面比它小的,Ar是一个数前面比它大的        add(j, 1);    }        memset(tr, 0, sizeof tr);        LL resA = 0, resV = 0;    for (int i = n; i; i -- )     {        int j = a[i];        Br[j] += ask(j - 1), Bl[j] += ask(n) - ask(j); //Br是后面比它小的,Bl是后面比它大的        add(j, 1);    }        for (int i = 1; i <= n; i ++ )     {        int j = a[i];        resA += (LL) Al[j] * Br[j];        resV += (LL) Ar[j] * Bl[j];    }        printf("%lld %lld\n", resV, resA);        return 0;}

代码2

#include #include #include using namespace std;using LL = long long;const int N = 2e5 + 10;int n, a[N], low[N], big[N], tr[N];void add(int x, int v) {    for (; x <= n; x += x & -x) tr[x] += v;}int ask(int x) {    int res = 0;    for (; x; x -= x & -x) res += tr[x];    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 ++ )     {        int j = a[i];        low[j] += ask(j - 1), big[j] += ask(n) - ask(j);        add(j, 1);    }        memset(tr, 0, sizeof tr);        LL resA = 0, resV = 0;    for (int i = n; i; i -- )     {        int j = a[i];        resA += (LL) low[j] * ask(j - 1);        resV += (LL) big[j] * (ask(n) - ask(j));        add(j, 1);    }    printf("%lld %lld\n", resV, resA);        return 0;}

\(\color{Green}{成功AC!}\)

关键词: 乘法原理 我们需要