算法分析
这题给出的数据范围是是10的5次方量级,因此应选择O(nlogn)或O(n*n)的算法,说明最多只能有一层循环,剩余时间复杂度用于判断和计数。
常规思路很容易想到二分法,二分法能够找到边界,且由于二分法的时间复杂度为O(logn),乘上循环所需要的O(n),刚好满足时间复杂度要求。
有个值得注意的地方是,若选择循环A数组,那么B数组元素的选择会影响到C数组元素的选择,脱离了乘法的独立性,就不能使用乘法来统计方案个数了。因此我们选择循环B数组,这样就能够在循环B数组时,使A的选择不影响C的选择,满足乘法的独立性。
模板二分法
首先递增排序(必须选择小于等于O(nlogn)的排序方法!),再找到每次循环B数组元素时满足条件的A数组的左边界,满足条件的C数组的右边界,通过边界范围内的个数相乘即得到每次选择不同B数组元素时的方案总数,循环累加即可。
C++ 代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int n, A[N], B[N], C[N];
long long res;
//注意空间数据范围,最坏情况下有N的三次方个方案,当N取最大时,数据会达到10的十五次方,爆出int范围
int main ()
{
cin >> n;
for(int i=1; i<=n; i++) cin >> A[i];
for(int i=1; i<=n; i++) cin >> B[i];
for(int i=1; i<=n; i++) cin >> C[i];
sort(A+1, A+n+1);
sort(B+1, B+n+1);
sort(C+1, C+n+1);
//快速排序O(nlogn)不怎么影响整体时间效率
for(int i=1; i<=n; i++) //循环B数组
{
int l = 1, r = n;
while(l < r) //标准y总左边界二分模板
{
int mid = l + r >> 1;
if(A[mid] >= B[i]) r = mid;
else l = mid + 1;
}
if(A[l] >= B[i]) l--; //特判,若不满足条件,则将下标左移
int t1 = l; // 保存A数组的下标
l = 1, r = n;
while(l < r) //标准y总右边界二分模板
{
int mid = l + r + 1 >> 1;
if(B[i] >= C[mid]) l = mid;
else r = mid - 1;
}
if(C[l] <= B[i]) l++; //特判,若不满足条件,则将下标右移
int t2 = l; //保存C数组的下标
res += (long long)t1 * (n - t2 + 1); //乘法原理
}
printf("%lld\n", res);
return 0;
}