1236. 递增三元组 Java题解 (前缀和,双指针)【第九届蓝桥杯省赛C++B组,JAVA B组】

输入样例:

3
1 1 1
2 2 2
3 3 3

输出样例:

27

解题思路:

因为数据规模为10^5,所以只能用一重循环,而通过最中间一行的遍历恰好能同时满足对前一行和后一行的遍历。设第一到第三行分别为:a[i],b[j],c[k],只需要找出在a数组中比b[j]小的元素个数,在c数组中比b[j]大的元素个数。

1. 前缀和:先将a数组中的元素映射到map中,map通过cnt数组来模拟,cnt[i] = j :就表示元素i的个数是j,再求前缀和数组s[i],也可以直接覆盖到cnt数组上,所以s[i] = j :就表示成了从0到i的元素个数是j个,当找小于数组中的某个数b[i]的个数时,直接查找前缀和数组cnt[b[i]-1]的值便可以直接求出。

因为前缀和数组计算时需要用到上个值的下标,所以将将三个数组中的每个元素值都加一可以避免数组越界。

2. 双指针: 需要对三个数组先排好序,通过移动上下两个指针从而求出符合条件的元素值的个数。也需要防止指针移动时发生越界。

Java代码:(前缀和)O(n)

import java.io.*;

public class Main {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		int N = 100000;
		
		int []a = new int[n + 1];
		int []b = new int[n + 1];
		int []c = new int[n + 1];
		String[] split = br.readLine().split(" ");
		for(int i = 1; i <= n; i++)
			a[i] = Integer.parseInt(split[i - 1]) + 1;
		split = br.readLine().split(" ");
		for(int i = 1; i <= n; i++)
			b[i] = Integer.parseInt(split[i - 1]) + 1;
		split = br.readLine().split(" ");
		for(int i = 1; i <= n; i++)
			c[i] = Integer.parseInt(split[i - 1]) + 1;
		
		int []cnta = new int[N + 1];//cnta[i] = j : 表示i出现了j次
		int []cntc = new int[N + 1];
		for(int i = 1; i <= n; i++) {
			cnta[a[i]]++;//相当于将a[i]哈希
			cntc[c[i]]++;
		}
		for(int i = 1; i <= N; i++) {//此时的cnt[i] = j 表示前缀和含义:不小于i的数共有j个
			cnta[i] += cnta[i - 1];
			cntc[i] += cntc[i - 1];
		}
		
		long ans = 0;//最多有n^3个:  10^15
		for(int i = 1; i <= n; i++) {
			ans += (long)cnta[b[i] - 1] * (n - cntc[b[i]]);//需要强转!!
		}
		System.out.println(ans);
	}
}

Java代码:(双指针)O(n)

import java.io.*;
import java.util.Arrays;

public class Main {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		
		int []a = new int[n + 1];
		int []b = new int[n + 1];
		int []c = new int[n + 1];
		String[] split = br.readLine().split(" ");
		for(int i = 1; i <= n; i++)
			a[i] = Integer.parseInt(split[i - 1]) + 1;
		split = br.readLine().split(" ");
		for(int i = 1; i <= n; i++)
			b[i] = Integer.parseInt(split[i - 1]) + 1;
		split = br.readLine().split(" ");
		for(int i = 1; i <= n; i++)
			c[i] = Integer.parseInt(split[i - 1]) + 1;
		
		Arrays.sort(a);
		Arrays.sort(b);
		Arrays.sort(c);
		
		int i = 1, k = 1;
		long ans = 0;
		for(int j = 1; j <= n; j++) {
			while(i <= n &&b[j] > a[i]) i++;//要防止下标越界
			while(k <= n && c[k] <= b[j]) k++;
			ans += (long) (i - 1) * (n - k + 1);//将最开始的1减掉
		}
		System.out.println(ans);
	}
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值