1236. 递增三元组
给定三个整数数组
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) 满足:
第一行包含一个整数 N。
第二行包含 N 个整数 A 1 , A 2 , … A N A_1,A_2,…A_N A1,A2,…AN。
第三行包含 N 个整数 B 1 , B 2 , … B N B_1,B_2,…B_N B1,B2,…BN。
第四行包含 N 个整数 C 1 , C 2 , … C N C_1,C_2,…C_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≤A_i,B_i,C_i≤10^5 0≤Ai,Bi,Ci≤105
3
1 1 1
2 2 2
3 3 3
27
使用三层 for 循环,第一层 for 循环依次枚举 A 数组里的每个元素,第二层 for 循环依次枚举 B 数组里的每个元素,第三层 for 循环依次枚举 C 数组里的每个元素,然后判断当前三个元素是否满足条件 A[i] < B[j] && B[j] < C[k],满足情况的计数。
但是如果数组最大长度为 1 0 5 10^5 105,遍历三个数组所需的时间 3 × 1 0 5 3 \times {10^5} 3×105,肯定会超时。
#include
#include
using namespace std;
const int N = 1e5 + 10;
int A[N], B[N], C[N];
int res = 0;
int n;
int main() {
cin >> n;
for(int i = 0; i < n; i++)
cin >> A[i];
for(int i = 0; i < n; i++)
cin >> B[i];
for(int i = 0; i < n; i++)
cin >> C[i];
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++;
cout << res;
return 0;
}
先对 A 数组 和 C 数组进行排序。
我们以 B 数组中的每个元素作为中间值,然后利用二分查找去 A 数组找到 < B[i] 的最大元素的位置,去 C 数组找到 > B[i] 的最小元素的位置,然后对应于每一个 B[i] 可构成的三元组个数为 l _ n u m × r _ n u m l\_num \times r\_num l_num×r_num,即 A 数组中 < B[i] 的元素的个数 * C 数组中 > B[i] 的元素的个数。
#include
#include
using namespace std;
const int N = 1e5 + 10;
int A[N], B[N], C[N];
int n;
long long res = 0;
int main() {
cin >> n;
for(int i = 0; i < n; i++) cin >> A[i];
for(int i = 0; i < n; i++) cin >> B[i];
for(int i = 0; i < n; i++) cin >> C[i];
sort(A, A + n);
sort(C, C + n);
for(int i = 0; i < n; i++) {
long long l_num = 0, r_num = 0;
long long l = 0, r = n - 1, mid;
while(l < r) {
mid = l + (r - l + 1) / 2;
if(A[mid] < B[i]) l = mid;
else r = mid - 1;
}
if(A[l] < B[i])
l_num = l + 1;
l = 0, r = n - 1;
while(l < r) {
mid = l + (r - l) / 2;
if(C[mid] > B[i]) r = mid;
else l = mid + 1;
}
if(C[l] > B[i])
r_num = n - l;
res += l_num * r_num;
}
cout << res;
return 0;
}
这里二分查找的算法可以用 STL 的内置函数来替代,代码更简洁一些。
lower_bound(A, A+n, B[i]) 函数返回 A 数组中 >= B[i] 的第一个元素的位置。
upper_bound(C, C+n, B[i]) 函数返回 C数组中 > B[i] 的第一个元素的位置。
#include
#include
using namespace std;
const int N = 1e5 + 10;
int A[N], B[N], C[N];
int n;
long long res = 0;
int main() {
cin >> n;
for(int i = 0; i < n; i++) cin >> A[i];
for(int i = 0; i < n; i++) cin >> B[i];
for(int i = 0; i < n; i++) cin >> C[i];
sort(A, A + n);
sort(C, C + n);
for(int i = 0; i < n; i++) {
long long l_num = 0, r_num = 0;
l_num = lower_bound(A, A + n, B[i]) - A;
r_num = n - (upper_bound(C, C + n, B[i]) - C);
res += l_num * r_num;
}
cout << res;
return 0;
}
我们以 B 数组的每一个 B[i] 作为中间数,用 cnt_A 数组来统计 A 数组中的元素出现的次数,用 cnt_C 数组来统计 C 数组中的元素出现的次数。然后再使用 AS,CS 两个数组来存放 cnt_A 和 cnt_C 的前缀和。
其中 AS[i] 和 CS[i] 表示 <= B[i] 的元素的个数,所以 A 数组中小于 B[i] 的元素个数为 AS[B[i] - 1],C 数组中大于 B[i] 的元素个数为 CS[N] - CS[B[i]],对于每一个 B[i] 求出 l_num 和 r_num,两者相乘,最后累加。
#include
#include
using namespace std;
const int N = 1e5 + 10;
int A[N], B[N], C[N];
int cnt_A[N], cnt_C[N];
long long AS[N], CS[N];
int n;
long long res = 0;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); cnt_A[A[i]] += 1; }
for(int i = 1; i <= n; i++) scanf("%d", &B[i]);
for(int i = 1; i <= n; i++) { scanf("%d", &C[i]); cnt_C[C[i]] += 1; }
AS[0] = cnt_A[0], CS[0] = cnt_C[0];
for(int i = 1; i <= N; i++) AS[i] = AS[i - 1] + cnt_A[i];
for(int i = 1; i <= N; i++) CS[i] = CS[i - 1] + cnt_C[i];
for(int i = 1; i <= n; i++) {
long long l_num = AS[B[i] - 1];
long long r_num = CS[N] - CS[B[i]];
res += l_num * r_num;
}
cout << res;
return 0;
}
先对三个数组都进行排序。
然后遍历 B 数组,以每个 B[i] 为中间数,使用双指针 l 和 r,用 l 来定位 A 数组中第一个大于等于 B[i] 的元素位置,此时的 l 即为 A 数组中小于 B[i] 的元素的个数 l_num;用 r 来定位 C 数组中第一个大于 B[i] 的元素位置,此时的 n - r 即为 C 数组中大于 B[i] 的元素的个数 r_num,最后对每个 B[i] 的 l_num 和 r_num 相乘最后累计求和。
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n;
int a[N], b[N], c[N];
int main()
{
cin>>n;
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
for(int i = 0; i < n; i++) scanf("%d", &b[i]);
for(int i = 0; i < n; i++) scanf("%d", &c[i]);
sort(a, a + n);
sort(b, b + n);
sort(c, c + n);
LL res = 0, l = 0, r = 0;
for(int i=0;i<n;i++)
{
while(l < n && a[l] < b[i]) l++;
while(r < n && c[r] <= b[i]) r++;
res += l * (n - r);
}
cout << res << endl;
}