1.题目
给定三个整数数组
A
=
[
A
1
,
A
2
,
…
A
N
]
,
A=[A1,A2,…AN],
A=[A1,A2,…AN],
B
=
[
B
1
,
B
2
,
…
B
N
]
,
B=[B1,B2,…BN],
B=[B1,B2,…BN],
C
=
[
C
1
,
C
2
,
…
C
N
]
,
C=[C1,C2,…CN],
C=[C1,C2,…CN],
请你统计有多少个三元组 (i,j,k) 满足:
1
≤
i
,
j
,
k
≤
N
1≤i,j,k≤N
1≤i,j,k≤N
A
i
<
B
j
<
C
k
Ai<Bj<Ck
Ai<Bj<Ck
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,…AN。
第三行包含 N 个整数 B1,B2,…BN。
第四行包含 N 个整数 C1,C2,…CN。
输出格式
一个整数表示答案。
数据范围
1
≤
N
≤
1
0
5
,
1≤N≤10^5,
1≤N≤105,
0
≤
A
i
,
B
i
,
C
i
≤
1
0
5
0≤Ai,Bi,Ci≤10^5
0≤Ai,Bi,Ci≤105
输入样例:
3
1 1 1
2 2 2
3 3 3
输出样例:
27
2.基本思想
暴力枚举 三重循环O(N^3) 肯定会超时!考虑优化
N N N为 1 0 5 10^5 105数量级,因而最多枚举三个数组中的一个;枚举 B B B数组,对于每个 B i B~i~ B i 数组的元素计算
①在 A A A中有多少个小于 B i Bi Bi
②在 B B B中有多少个大于 B i Bi Bi 得到的相乘即可!
法一: S o r t + 二分 Sort+二分 Sort+二分 O ( N l o g N ) O(NlogN) O(NlogN)
法二:前缀和 O ( N ) O(N) O(N)时间换空间
以①为列:创建cnt[i]
表示在A数组中i这个值出现次数
前缀和数组s[i]
表示cnt[0]+cnt[1]+……+cnt[i]
即在A中0~i
出现的次数
所求①即为:s[b[i-1]]
②同理可得
3.代码实现
暴力枚举O(N^3)
import java.util.Scanner;
public class _1216递增三元组 {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
int N = sc.nextInt();
int a[] = new int[N];
int b[] = new int[N];
int c[] = new int[N];
for (int i = 0; i < N; i++)
a[i] = sc.nextInt();
for (int i = 0; i < N; i++)
b[i] = sc.nextInt();
for (int i = 0; i < N; i++)
c[i] = sc.nextInt();
int res = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
for (int k = 0; k < N; k++)
if (a[i] < b[j] && b[j] < c[k])
res++;
System.out.println(res);
}
}
前缀和 O ( N ) O(N) O(N)
import java.util.Scanner;
public class _1216递增三元组 {
static Scanner sc = new Scanner(System.in);
static int N = 100010;
static int a[] = new int[N];
static int b[] = new int[N];
static int c[] = new int[N];
static int cnt[] = new int[N];//cnt[i]表示在A数组中i这个值出现次数
static int cnt1[] = new int[N];//cnt[i]表示在C数组中i这个值出现次数
static int as[] = new int[N];//as[i]表示在a[]中有多少个数字小于b[i]
static int cs[] = new int[N];//cs[i]表示在c[]中有多少个数字大于b[i]
static int s[] = new int[N];//前缀和数组 s[i]表示cnt[o]+cnt[1]+……+cnt[i] 即在A中0~i出现的次数
static int s1[] = new int[N];//前缀和数组 s[i]表示cnt[o]+cnt[1]+……+cnt[i] 即在B中0~i出现的次数
public static void main(String[] args) {
int n = sc.nextInt();
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
a[i]++;
}
for (int i = 0; i < n; i++) {
b[i] = sc.nextInt();
b[i]++;
}
for (int i = 0; i < n; i++) {
c[i] = sc.nextInt();
c[i]++;
}
//求as[]
for (int i = 0; i < n; i++) cnt[a[i]]++;//记录a[i]出现的次数
for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];//求cnt[]的前缀和
for (int i = 0; i < n; i++) as[i] = s[b[i] - 1];
//求cs[i]
for (int i = 0; i < n; i++) cnt1[c[i]]++;//记录c[i]出现的次数
for (int i = 1; i < N; i++) s1[i] = s1[i - 1] + cnt1[i];
for (int i = 0; i < n; i++) cs[i] = s1[N - 1] - s1[b[i]];
//枚举每个b[i]
long res = 0;
for (int i = 0; i < n; i++) res += (long) as[i] * cs[i];
System.out.println(res);
}
sort+二分 O ( N l o g N ) O(NlogN) O(NlogN)
import java.util.Arrays;
import java.util.Scanner;
public class _1216递增三元组 {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
int N = sc.nextInt();
int a[] = new int[N];
int b[] = new int[N];
int c[] = new int[N];
for (int i = 0; i < N; i++)
a[i] = sc.nextInt();
for (int i = 0; i < N; i++)
b[i] = sc.nextInt();
for (int i = 0; i < N; i++)
c[i] = sc.nextInt();
Arrays.sort(a);
Arrays.sort(b);
Arrays.sort(c);
long res = 0;// 注意 long 最终结果数据量很大
for (int i = 0; i < N; i++) { // a < b < c 遍历数组b只需要一次循
long ai = 0, ci = 0;
int l = 0, r = N - 1;
//二分查找 满足 a[i]<b[i] 的元素索引
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] < b[i]) l = mid;
else r = mid - 1;
}
if (a[l] >= b[i]) continue;//特殊 即所有数都不满足时
//保存满足条件的a[]中个数 即索引数+1
ai = l + 1;
//恢复
l = 0;
r = N - 1;
//二分查找 满足 c[i]>b[i] 的元素索引
while (l < r) {
int mid = l + r >> 1;
if (c[mid] > b[i]) r = mid;
else l = mid + 1;
}
if (c[l] <= b[i]) l = N;//结束数字都小于n 取 N是为了使得 N-l为0 便于运算 (continue一样)
ci = N - l; //保存满足条件的c[]中个数 即N-l
res += ai * ci;
}
System.out.println(res);
}
}