AcWing241. 楼兰图腾
题目大意
\(\qquad\)\(1.\)给你一个序列,让你统计三个数\(i,j,k\)(\(i <j<k\)),当这三个数满足\(a_i>a_j且a_j<a_k\)的对数
\(\qquad\)\(2.\)同样地统计\(i,j,k(i<j<k)\),求满足\(a_i<a_j, a_j>a_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 <iostream>
#include <cstring>
#include <algorithm>
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 <iostream>
#include <cstring>
#include <algorithm>
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;
}

浙公网安备 33010602011771号