1236. 递增三元组
题目难度:中等
题目来源:第九届蓝桥杯省赛C++ B组,第九届蓝桥杯省赛Java B组
给定三个整数数组
A = [ A 1 , A 2 , … A N ] A = [A_1, A_2, … A_N] A=[A1,A2,…AN],
B = [ B 1 , B 2 , … B N ] B = [B_1, B_2, … B_N] B=[B1,B2,…BN],
C = [ C 1 , C 2 , … C N ] C = [C_1, C_2, … C_N] C=[C1,C2,…CN],
请你统计有多少个三元组 ( i , j , k ) (i, j, k) (i,j,k) 满足:
第一行包含一个整数 N N N。
第二行包含 N N N 个整数 A 1 , A 2 , … A N A_1, A_2, … A_N A1,A2,…AN。
第三行包含 N N N 个整数 B 1 , B 2 , … B N B_1, B_2, … B_N B1,B2,…BN。
第四行包含 N N N 个整数 C 1 , C 2 , … C N C_1, C_2, … C_N C1,C2,…CN。
一个整数表示答案。
1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1≤N≤105,
0 ≤ A i , B i , C i ≤ 1 0 5 0 \le A_i,B_i,C_i \le 10^5 0≤Ai,Bi,Ci≤105
3
1 1 1
2 2 2
3 3 3
27
这道题的意思很清楚,就是从A、B、C三个数组中选出三个数满足严格小于关系,问满足条件的三元组的个数
对于枚举类型的题目,最重要的就是枚举的顺序,要注意不重不漏,其次就是要对代码进行优化,利用题目性质,等式,进行优化
如果我们从暴力来做就是用三重循环,直接判断计数
这里考虑到这个不等式的关系,就是A小于B小于C,如果我们枚举的是A或者C,那么对于我们统计的B和C的数量,是无法简单的使用乘法原理直接相乘的,因为乘法原理是要求独立的,因此我们在这里是需要枚举B的,对于每一个B只需要在计算满足要求的A的个数,满足要求的C的个数,再相乘即可
那么这里的问题就是,如何快速计算出满足要求的A和C的个数,这其实是同一个问题了
我们将其抽象出来就是,在A中有多少个数小于B
我们能想到的第一个方法就是使用前缀和,第二个方法就是排序+二分,第三种方法就是使用双指针算法,我们暂时就先只使用前两个方法来做
我们首先开一个count数组,表示在A数组中,i的值出现的次数
然后我们再对这个count数组做前缀和,他的含义就是在A中从0到i的数字出现了多少次
#include
#include
using namespace std;
using ll = long long;
const int N = 100010;
int n;
int a[N], b[N], c[N];
int ap[N], cp[N]; // a和c的前缀
int cnt[N], s[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i], a[i]++;
for (int i = 0; i < n; i++) cin >> b[i], b[i]++;
for (int i = 0; i < n; i++) cin >> c[i], c[i]++;
// 计数+前缀和
for (int i = 0; i < n; i++) cnt[a[i]]++;
for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];
for (int i = 0; i < n; i++) ap[i] = s[b[i] - 1];
memset(cnt, 0, sizeof(cnt));
memset(s, 0, sizeof(s));
for (int i = 0; i < n; i++) cnt[c[i]]++;
for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];
for (int i = 0; i < n; i++) cp[i] = s[N - 1] - s[b[i]];
ll ans = 0;
for (int i = 0; i < n; i++) ans += (ll)ap[i] * cp[i];
cout << ans << '\n';
return 0;
}