Acwing---1236.递增三元组

1.题目

给定三个整数数组

A = [ A 1 , A 2 , … A N ] , A=[A1,A2,…AN], A=[A1,A2,AN],
B = [ B 1 , B 2 , … B N ] , B=[B1,B2,…BN], B=[B1,B2,BN],
C = [ C 1 , C 2 , … C N ] , C=[C1,C2,…CN], C=[C1,C2,CN],

请你统计有多少个三元组 (i,j,k) 满足:

1 ≤ i , j , k ≤ N 1≤i,j,k≤N 1i,j,kN
A i < B j < C k Ai<Bj<Ck Ai<Bj<Ck

输入格式
第一行包含一个整数 N。

第二行包含 N 个整数 A1,A2,…AN。

第三行包含 N 个整数 B1,B2,…BN。

第四行包含 N 个整数 C1,C2,…CN。

输出格式
一个整数表示答案。

数据范围
1 ≤ N ≤ 1 0 5 , 1≤N≤10^5, 1N105,
0 ≤ A i , B i , C i ≤ 1 0 5 0≤Ai,Bi,Ci≤10^5 0Ai,Bi,Ci105

输入样例:

3
1 1 1
2 2 2
3 3 3

输出样例:

27

2.基本思想

暴力枚举 三重循环O(N^3) 肯定会超时!考虑优化

N N N 1 0 5 10^5 105数量级,因而最多枚举三个数组中的一个;枚举 B B B数组,对于每个 B   i   B~i~ B i 数组的元素计算
①在 A A A中有多少个小于 B i Bi Bi
②在 B B B中有多少个大于 B i Bi Bi 得到的相乘即可!

法一: S o r t + 二分 Sort+二分 Sort+二分 O ( N l o g N ) O(NlogN) O(NlogN)
法二:前缀和 O ( N ) O(N) O(N) 时间换空间
以①为列:创建cnt[i]表示在A数组中i这个值出现次数
前缀和数组 s[i]表示cnt[0]+cnt[1]+……+cnt[i] 即在A中0~i出现的次数
所求①即为:s[b[i-1]]
②同理可得

3.代码实现

暴力枚举O(N^3)

import java.util.Scanner;

public class _1216递增三元组 {
    static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        int N = sc.nextInt();
        int a[] = new int[N];
        int b[] = new int[N];
        int c[] = new int[N];
        for (int i = 0; i < N; i++)
            a[i] = sc.nextInt();
        for (int i = 0; i < N; i++)
            b[i] = sc.nextInt();
        for (int i = 0; i < N; i++)
            c[i] = sc.nextInt();
        int res = 0;
        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++;

        System.out.println(res);
    }
}

前缀和 O ( N ) O(N) O(N)

import java.util.Scanner;

public class _1216递增三元组 {
    static Scanner sc = new Scanner(System.in);
    static int N = 100010;
    static int a[] = new int[N];
    static int b[] = new int[N];
    static int c[] = new int[N];

    static int cnt[] = new int[N];//cnt[i]表示在A数组中i这个值出现次数
    static int cnt1[] = new int[N];//cnt[i]表示在C数组中i这个值出现次数
    static int as[] = new int[N];//as[i]表示在a[]中有多少个数字小于b[i]
    static int cs[] = new int[N];//cs[i]表示在c[]中有多少个数字大于b[i]
    static int s[] = new int[N];//前缀和数组 s[i]表示cnt[o]+cnt[1]+……+cnt[i] 即在A中0~i出现的次数
    static int s1[] = new int[N];//前缀和数组 s[i]表示cnt[o]+cnt[1]+……+cnt[i] 即在B中0~i出现的次数

    public static void main(String[] args) {
        int n = sc.nextInt();


        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
            a[i]++;
        }
        for (int i = 0; i < n; i++) {
            b[i] = sc.nextInt();
            b[i]++;
        }
        for (int i = 0; i < n; i++) {
            c[i] = sc.nextInt();
            c[i]++;
        }
        
        //求as[]
        for (int i = 0; i < n; i++) cnt[a[i]]++;//记录a[i]出现的次数
        for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];//求cnt[]的前缀和
        for (int i = 0; i < n; i++) as[i] = s[b[i] - 1];

        //求cs[i]
        for (int i = 0; i < n; i++) cnt1[c[i]]++;//记录c[i]出现的次数
        for (int i = 1; i < N; i++) s1[i] = s1[i - 1] + cnt1[i];
        for (int i = 0; i < n; i++) cs[i] = s1[N - 1] - s1[b[i]];

        //枚举每个b[i]
        long res = 0;
        for (int i = 0; i < n; i++) res += (long) as[i] * cs[i];

        System.out.println(res);
    }

sort+二分 O ( N l o g N ) O(NlogN) O(NlogN)

import java.util.Arrays;
import java.util.Scanner;

public class _1216递增三元组 {
    static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        int N = sc.nextInt();
        int a[] = new int[N];
        int b[] = new int[N];
        int c[] = new int[N];
        for (int i = 0; i < N; i++)
            a[i] = sc.nextInt();
        for (int i = 0; i < N; i++)
            b[i] = sc.nextInt();
        for (int i = 0; i < N; i++)
            c[i] = sc.nextInt();
        Arrays.sort(a);
        Arrays.sort(b);
        Arrays.sort(c);
        long res = 0;// 注意 long  最终结果数据量很大
        for (int i = 0; i < N; i++) {  // a < b < c 遍历数组b只需要一次循
            long ai = 0, ci = 0;
            int l = 0, r = N - 1;
            //二分查找 满足 a[i]<b[i] 的元素索引
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (a[mid] < b[i]) l = mid;
                else r = mid - 1;
            }
            if (a[l] >= b[i]) continue;//特殊 即所有数都不满足时
            //保存满足条件的a[]中个数 即索引数+1
            ai = l + 1;
            //恢复
            l = 0;
            r = N - 1;
            //二分查找 满足 c[i]>b[i] 的元素索引
            while (l < r) {
                int mid = l + r >> 1;
                if (c[mid] > b[i]) r = mid;
                else l = mid + 1;
            }
            if (c[l] <= b[i]) l = N;//结束数字都小于n 取 N是为了使得 N-l为0 便于运算  (continue一样)
            ci = N - l;     //保存满足条件的c[]中个数 即N-l
            res += ai * ci;
        }
        System.out.println(res);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

amant 柒少

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值