A=[A1,A2,…AN],
B=[B1,B2,…BN],
C=[C1,C2,…CN],
请你统计有多少个三元组 (i,j,k) 满足:
- 1≤i,j,k≤N
- Ai<Bj<Ck
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,…AN。
第三行包含 N 个整数 B1,B2,…BN。
第四行包含 N 个整数 C1,C2,…CN。
输出格式
一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai,Bi,Ci≤105
输入样例:
3
1 1 1
2 2 2
3 3 3
输出样例:
27
暴力做法:
#include<iostream> // 输入输出操作
#include<cstdio> // 输入输出函数
#include<algorithm> // 标准排序函数
#include<cstring> // 字符串处理函数
using namespace std;
typedef long long LL; // 将 long long 定义为 LL
const int N=1e5+10; // 将常量 N 定义为 1e5+10
int n; // 元素个数
int a[N], b[N], c[N]; // 元素数组
signed main()
{
cin>>n; // 输入 n 的值
for(int i=0;i<n;i++) scanf("%d", &a[i]); // 输入数组 a 的元素
for(int i=0;i<n;i++) scanf("%d", &b[i]); // 输入数组 b 的元素
for(int i=0;i<n;i++) scanf("%d", &c[i]); // 输入数组 c 的元素
LL res=0; // 将结果初始化为 0
for(int i=0;i<n;i++) {
int count_a = 0, count_c = 0;
for(int j=0;j<n;j++) {
if(a[j] < b[i]) count_a++;
if(c[j] > b[i]) count_c++;
}
res += (LL)count_a * count_c;
}
cout<<res<<endl; // 输出结果
}
前缀和
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n;
int a[N], b[N], c[N];
int s[N], aa[N], cc[N];
int main()
{
cin>>n;
//s前缀和数组,是以值为下标,在那一位置加一
for(int i=0;i<n;i++) scanf("%d", &a[i]), a[i]++;//前缀和下表从1开始
for(int i=0;i<n;i++) scanf("%d", &b[i]), b[i]++;
for(int i=0;i<n;i++) scanf("%d", &c[i]), c[i]++;
for(int i=0;i<n;i++) s[a[i]]++;//s数组存的是a值在这一位置的数
for(int i=1;i<N;i++) s[i]+=s[i-1];
for(int i=0;i<n;i++) aa[i]=s[b[i]-1];//aa存储的是当前下标,小于b[i]的数的个数,即为s[b[i]-1]
memset(s, 0, sizeof s);
for(int i=0;i<n;i++) s[c[i]]++;
for(int i=1;i<N;i++) s[i]+=s[i-1];
for(int i=0;i<n;i++) cc[i]=s[N-1]-s[b[i]];
LL res=0;
for(int i=0;i<n;i++) res+=(LL)aa[i]*cc[i];//枚举b
cout<<res<<endl;
return 0;
}
这个代码的思路是为了解决一个特定的问题,即在给定的三个数组 `a`, `b`, `c` 中,找到满足条件 `a[i] < b[j] < c[k]` 的元素对 `(i, j, k)` 的数量。这个思路的核心在于使用前缀和来优化查找过程,使得对于每个 `b[j]`,我们能够快速找到所有满足条件的 `a[i]` 和 `c[k]`。
代码解释:
1. **前缀和数组的应用**:
- 代码使用前缀和数组 `s` 来记录每个值在数组 `a` 和 `c` 中出现的次数。前缀和数组 `s` 的索引代表值,而 `s[i]` 代表值小于等于 `i` 的元素个数。
- 通过前缀和,我们可以快速计算任意区间内元素的个数,这对于统计满足条件的元素对非常有效。
2. **处理数组 `a` 和 `c`**:
- 首先,代码读取数组 `a` 和 `c` 的元素,并对每个元素加一,这是因为前缀和数组 `s` 的索引从 `1` 开始。
- 然后,代码更新前缀和数组 `s`,使得 `s[a[i]]` 和 `s[c[i]]` 增加 `1`,表示值 `a[i]` 和 `c[i]` 出现了 `1` 次。
- 接着,计算 `s` 的累积和,这样 `s[i]` 就存储了值小于等于 `i` 的元素个数。
3. **计算 `aa` 和 `cc` 数组**:
- 对于数组 `b`,代码计算 `aa[i]`,即小于 `b[i]` 的元素个数,通过 `s[b[i]-1]` 获取。
- 对于数组 `c`,代码计算 `cc[i]`,即大于 `b[i]` 的元素个数,通过 `s[N-1]-s[b[i]]` 获取。
4. **计算结果**:
- 最后,代码遍历数组 `b`,对于每个 `b[j]`,计算满足条件的 `a[i]` 和 `c[k]` 的对数,即 `aa[j] * cc[j]`,并将这些对数累加到结果变量 `res` 中。
5. **输出结果**:
- 代码输出最终的结果 `res`,即满足 `a[i] < b[j] < c[k]` 的元素对 `(i, j, k)` 的总数。
这个思路之所以有效,是因为它利用了前缀和数组来快速计算区间内元素的个数,从而避免了嵌套循环,大大提高了算法的效率。此外,通过清零并重用前缀和数组 `s`,代码还优化了空间复杂度。