1.二分
思路:由时间复杂度分析算法,由于它的数据范围是10^5,最先想到的是三重循环,但是若算法中超过两重循环的话(即10^10以上,一般控制在$O(10^7到10^8)$才不会超时),说明算法最坏得用$O(nlogn)$才不会超时。
通过上面分析说明最多枚举一个数组然后里面操作$O(logn)$的算法,刚好二分就符合该复杂度;
然后是遍历哪个呢,遍历A和遍历C一样,若遍历A的话,B和C就会相互影响两个又得多加些操作。
这时候就只能遍历B使得A和C的选择相互独立,用二分选择最大小于B的数A和最小大于B的数C然后两者相乘得n,表示以这个数B放中间有n种分配
注意事项:
1.题目中是“Ai<Bj<Ck”,不包含等于的情况,在代码判断中切记不要忘记了!!!
2.用整型会溢出,因为数据范围很大又要相乘说明有很多种选择方案,可能会爆int得用long存储
3.java里 int 类整数的最大值是 2 的 31 次方 - 1 = 2147483648 - 1 = 2147483647
long的最大值是2^63-1,值是9223372036854775807
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int[] a=new int[n],b=new int[n],c=new int[n];
for(int i=0;i<n;i++)a[i]=scanner.nextInt();
for(int i=0;i<n;i++)b[i]=scanner.nextInt();
for(int i=0;i<n;i++)c[i]=scanner.nextInt();
Arrays.sort(a);Arrays.sort(b);Arrays.sort(c);//要用二分就得先从小到大排序
long sum=0;
for(int i=0;i<n;i++) {
int l=0,r=n-1;
while(l<r) {
int mid=l+r+1>>1;
if(a[mid]<b[i])l=mid;//找最后一个数是小于x的下标,让选择范围一直往右边缩小
else r=mid-1;
}
if(a[l]>=b[i])continue;//若找出来的数不满足a<b
int x=l;
l=0;r=n-1;
while(l<r) {
int mid=l+r>>1;
if(c[mid]>b[i])r=mid;//找第一个数是大于数b的下标,让选择范围一直往左边缩小
else l=mid+1;
}
if(c[l]<=b[i])continue;//若找出来的数不满足c<b
int y=l;
sum+=(long)(x+1)*(n-y);//题目数据范围很大得用long存取
}
System.out.println(sum);
2.前缀和
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int N=100010;
int[] a=new int[N],b=new int[N],c=new int[N];
int[] cnta=new int[N],cntc=new int[N];//cnta[i]记录在数组a中,出现i的次数
int[] as=new int[N],cs=new int[N];//as[i]表示前i个数出现在数组a中的次数总和
for(int i=1;i<=n;i++)a[i]=scanner.nextInt()+1;//因为取值范围是0开始,为了防止下标越界都加上一,对结果无影响
for(int i=1;i<=n;i++)b[i]=scanner.nextInt()+1;
for(int i=1;i<=n;i++)c[i]=scanner.nextInt()+1;
long sum=0;
for(int i=1;i<=n;i++) {//分别记录出现在ac数组中各个数出现的次数
cnta[a[i]]++;
cntc[c[i]]++;
}
for(int i=1;i<N;i++) {//求前缀和
as[i]=cnta[i]+as[i-1];
cs[i]=cntc[i]+cs[i-1];
}
for(int i=1;i<=n;i++) {
int x=as[b[i]-1];//b[i]本来可能取0的但是前面加一了,就不会越界了,在数组a中小于b[i]的总个数
int y=cs[100001]-cs[b[i]];//在数组c中大于b[i]的总个数,若不用前缀和,得双重循环判断就会超时
sum+=(long)x*y;
}
System.out.println(sum);