2018.4.3 遇到一个很有意思的题目
题目:递增三元组
给定三个整数数组
A = [A1, A2, … AN],
B = [B1, B2, … BN],
C = [C1, C2, … CN],
请你统计有多少个三元组(i, j, k) 满足:
1. 1 <= i, j, k <= N
2. Ai < Bj < Ck
【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, … AN。
第三行包含N个整数B1, B2, … BN。
第四行包含N个整数C1, C2, … CN。
对于30%的数据,1 <= N <= 100
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000
【输出格式】
一个整数表示答案
【样例输入】
3
1 1 1
2 2 2
3 3 3
【样例输出】
27
因为是 100000 的数据,如果暴力三重 for 循环只能过 30%
O( N ) 的做法(1)
可以利用前缀和,也就是类似于桶排序的标记法,每次出现一个元素,就在这个元素的桶上做标记,例如
2 4 7 8 9 2
那么形成的桶就是
1 2 3 4 5 6 7 8 9
0 2 0 1 0 0 1 1 1
很明显就是统计每一个出现的元素的次数
然后,问小于 5 的数有多少个 ?
不就是 2 个 2 , 1 个 4 吗,恰好 3 个数 < 5
问小于 6 的数有多少 ?
2 + 1 = 3 个
用前缀和的思想看这些桶
1 2 3 4 5 6 7 8 9
0 2 2 3 3 3 4 5 6
问,多少个数 < 5
就是 5-1 = 4 ,小于等于 4 的数有 3 个,也就是 小于 5 的数有 3 个
同理,后缀和
这道题目,可以先看 B 行,对B行每一个元素, pre[ B[i]-1 ] 这个地方就是A 行中有多少个比 B[i] 小的, Net[ B[i]+1 ] 就是C行中有多少个比 B[i] 大的。
很奇妙,复杂度 O( N )
#include <bits/stdc++.h> using namespace std ; #define rep( i , j , n ) for ( int i = int(j) ; i <= int(n) ; ++i ) #define dew( i , j , n ) for ( int i = int(n-1) ; i >= int(j) ; --i ) const int N = 1e6 + 7 ; int A[N] , B[N] , C[N] ; int pre[N] , Net[N] ; int main () { int n ; while ( ~scanf ( "%d" , &n ) ) { memset ( pre , 0 , sizeof ( pre ) ) ; memset ( Net , 0 , sizeof ( Net ) ) ; rep ( i , 1 , n ) scanf ( "%d" , &A[i] ) , ++pre[ A[i] ] ; rep ( i , 1 , n ) scanf ( "%d" , &B[i] ) ; rep ( i , 1 , n ) scanf ( "%d" , &C[i] ) , ++Net[ C[i] ] ; rep ( i , 1 , *max_element ( A + 1 , A + n + 1 ) ) pre[i] += pre[i-1] ; dew ( i , 1 , *max_element ( C + 1 , C + n + 1 ) ) Net[i] += Net[i+1] ; long long ans = 0 ; rep ( i , 1 , n ) ans += pre[ B[i] - 1 ] * Net[ B[i] + 1 ] ; cout << ans << endl ; } return 0 ; }
时间复杂度 O (N) ,不过会受到数据大小的限制,因为,如果数据可以是 long long , 或者说 10000000000 这样的数字,是开不出 pre , Net 数组的。
O( N ) 的做法(2)
还有一种 O(N) 的解法。
和上面的类似。不过这里是直接利用桶排序,因为数据 < 100000, 用桶排序复杂度稳稳的 O(N)。
对 A , B , C 排序之后
同样是以 B 行为出发点,在 A 行设置一个指针(不一定是指针,某个递增量即可),在 C 行也设置一个指针,对 B 的每一个元素,比较 A , C 当前指针处的值,移动到适当位置,使得A指针以前的都比 B[i] 小,C指针以后的值都比 B[i] 大,既然分好区了,那就直接区间长度相乘。
因为最多就是访问完三个数组,复杂度 3N,也就是 O( N ) 。
#include <bits/stdc++.h> using namespace std ; #define rep( i , j , n ) for ( int i = int(j) ; i <= int(n) ; ++i ) #define dew( i , j , n ) for ( int i = int(n-1) ; i >= int(j) ; --i ) const int N = 1e6 + 7 ; const int M = 1e5 + 7 ; int A[N] , B[N] , C[N] ; int n , A_cnt , C_cnt ; int book[M] ; ; void Bucket_sort ( int *a , int n ) { // 桶排序, 数据不大的情况, 复杂度 O(N) int top = 0 ; int max_one = *max_element ( a + 1 , a + n + 1 ) ; int min_one = *min_element ( a + 1 , a + n + 1 ) ; memset ( book , 0 , sizeof ( book ) ) ; rep ( i , 1 , n ) ++book[ a[i] ] ; rep ( i , min_one , max_one ) rep ( j , 1 , book[i] ) // 看 i 这个数出现了多少次 a[++top] = i ; // 出现过的数字直接放进原数组 } int main () { while ( ~scanf ( "%d" , &n ) ) { rep ( i , 1 , n ) scanf ( "%d" , &A[i] ) ; rep ( i , 1 , n ) scanf ( "%d" , &B[i] ) ; rep ( i , 1 , n ) scanf ( "%d" , &C[i] ) ; Bucket_sort ( A , n ) ; Bucket_sort ( B , n ) ; Bucket_sort ( C , n ) ; long long ans = 0 ; A_cnt = C_cnt = 1 ; rep ( i , 1 , n ) { while ( A_cnt < n && A[A_cnt] < B[i] ) ++A_cnt ; while ( C_cnt < n && C[C_cnt] <= B[i] ) ++C_cnt ; int less = B[i] > A[n] ? n : A_cnt - 1 ; int more = B[i] > C[n] ? 0 : n + 1 - C_cnt ; ans += less * more ; } cout << ans << endl ; } return 0 ; }
O ( N log N ) 解法
从 B 行开始,每一个元素,在 A 行二分搜索找到最后一个小于自己的数的位置
在 C行二分搜索第一个大于自己的数的位置
根据 Ai < Bj < Ck
复杂度 O( N log N )
#include <bits/stdc++.h> using namespace std ; #define rep( i , j , n ) for ( int i = int(j) ; i <= int(n) ; ++i ) #define dew( i , j , n ) for ( int i = int(n-1) ; i >= int(j) ; --i ) const int N = 1e6 + 7 ; int A[N] , B[N] , C[N] ; int n ; int Binary_less ( int *a , int len , int key ) { int l = 1 , r = len ; while ( l <= r ) { int mid = ( l + r ) >> 1 ; if ( key <= a[mid] ) r = mid - 1 ; else l = mid + 1 ; } return r ; } int Binary_more ( int *a , int len , int key ) { int l = 1 , r = len ; while ( l <= r ) { int mid = ( l + r ) >> 1 ; if ( key < a[mid] ) r = mid - 1 ; else l = mid + 1 ; } return l ; } int main () { while ( ~scanf ( "%d" , &n ) ) { rep ( i , 1 , n ) scanf ( "%d" , &A[i] ) ; rep ( i , 1 , n ) scanf ( "%d" , &B[i] ) ; rep ( i , 1 , n ) scanf ( "%d" , &C[i] ) ; sort ( A+1 , A+n+1 ) ; sort ( C+1 , C+n+1 ) ; long long ans = 0 ; rep ( i , 1 , n ) { int less = Binary_less ( A , n , B[i] ) ; int more = n + 1 - Binary_more ( C , n , B[i] ) ; ans += less * more ; } cout << ans << endl ; } return 0 ; }