AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 应用
  • 更多
    • 题解
    • 分享
    • 商店
    • 问答
    • 吐槽
  • App
  • 登录/注册

AcWing 1236. 递增三元组    原题链接    中等

作者: 作者的头像   小呆呆 ,  2020-01-06 01:00:07 ,  所有人可见 ,  阅读 6280


37


14

算法1 (前缀和)

  • 1、acnt[i]表示在A中i这个值出现多少次,ccnt[i]表示在C中i这个值出现多少次

  • 2、关键的一步,通过s[]记录当前数组从1到i中,所有数出现的次数的前缀和

  • 3、as[]记录在A中由多少个数小于B[i],as[i] = s[b[i] - 1]

  • 4、cs[]记录在C中由多少个数大于B[i], cs[i] = s[N - 1] - s[b[i]]

  • 5、由于a[]和c[]互斥,通过乘法原理可得res += as[i] * cs[i]

时间复杂度 $O(n)$

参考文献

算法提高课

Java 代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
    static int N = 100010;
    static int[] a = new int[N];
    static int[] b = new int[N];
    static int[] c = new int[N];
    static int[] acnt = new int[N];//acnt[i]表示在A中i这个值出现多少次
    static int[] ccnt = new int[N];//ccnt[i]表示在C中i这个值出现多少次
    static int[] as = new int[N];//记录在A中由多少个数小于B[i]
    static int[] cs = new int[N];//记录在C中由多少个数大于B[i]
    static int[] s = new int[N];//求从1到i中,所有数出现的次数的前缀和
    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine().trim());
        String[] s1 = reader.readLine().split(" ");
        String[] s2 = reader.readLine().split(" ");
        String[] s3 = reader.readLine().split(" ");
        for(int i = 1;i <= n;i++) a[i] = Integer.parseInt(s1[i - 1]) + 1;
        for(int i = 1;i <= n;i++) b[i] = Integer.parseInt(s2[i - 1]) + 1;
        for(int i = 1;i <= n;i++) c[i] = Integer.parseInt(s3[i - 1]) + 1;

        //求as[]
        for(int i = 1;i <= n;i++) acnt[a[i]] ++;
        for(int i = 1;i <= N - 1;i++) s[i] = s[i - 1] + acnt[i];
        for(int i = 1;i <= n;i++) as[i] = s[b[i] - 1];

        //求cs[]
        for(int i = 1;i <= n;i++) ccnt[c[i]] ++;
        for(int i = 1;i <= N - 1;i++) s[i] = s[i - 1] + ccnt[i];
        for(int i = 1;i <= n;i++) cs[i] = s[N - 1] - s[b[i]];

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

算法2(二分法)

  • 1、对3个数组分别进行从小到大排序

  • 2、找到a[]数组中最小的大于等于bi的元素位置la,便可知小于bi的个数为num1

  • 3、找到c[]数组中最大的小于等于bi的元素位置lb,便可知大于bi的个数为num2

  • 4、由于a[]和c[]互斥,通过乘法原理可知符合条件的个数为num1 * num2
    注意:当la == 0 或者 lb == n + 1时,则表示不符合条件

时间复杂度 $O(nlogn)$

参考文献

算法提高课

Java 代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
    static int N = 100010;
    static int[] a = new int[N];
    static int[] b = new int[N];
    static int[] c = new int[N];
    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine().trim());
        String[] s1 = reader.readLine().split(" ");
        String[] s2 = reader.readLine().split(" ");
        String[] s3 = reader.readLine().split(" ");
        for(int i = 1;i <= n;i++) a[i] = Integer.parseInt(s1[i - 1]);
        for(int i = 1;i <= n;i++) b[i] = Integer.parseInt(s2[i - 1]);
        for(int i = 1;i <= n;i++) c[i] = Integer.parseInt(s3[i - 1]);
        Arrays.sort(a,1,n + 1);
        Arrays.sort(b,1,n + 1);
        Arrays.sort(c,1,n + 1);
        long res = 0;
        //枚举b[]数组
        for(int i = 1;i <= n;i++)
        {
            //找到a[]数组中最小的大于等于bi的元素位置
            int la = 0,ra = n + 1;
            while(la < ra)
            {
                int mid = (la + ra) >> 1;
                if(a[mid] >= b[i]) ra = mid;
                else la = mid + 1;
            }
            //找到c[]数组中最大的小于等于bi的元素位置
            int lb = 0,rb = n + 1; 
            while(lb < rb)
            {
                int mid = (lb + rb + 1) >> 1;
                if(c[mid] <= b[i]) lb = mid;
                else rb = mid - 1;
            }
            if(la == 0 || lb == n + 1) continue;
            res += ((long)(la - 1) *(n - lb)); 
        }
        System.out.println(res);
    }
}


24 评论


用户头像
雨木木   2022-03-21 18:35         踩      回复

大佬,想问一下,啥时候用个split() 啥时候用个trim(),还有就是啥时候readLine()啥时候nextLine()


用户头像
写的好啊我是废物   2022-02-03 17:04         踩      回复

for(int i = 1;i <= n;i++) cs[i] = s[N - 1] - s[b[i]];
为什么在求比b[i]大的数时是s[N-1],不是s[N].

用户头像
一个人的巴黎   10个月前         踩      回复

看你s[]是定义成s[N]还是s[N+1]了,如果是后者你这么写就没问题了


用户头像
玉米最强大   2021-03-14 20:59         踩      回复

for(int i = 1;i <= N - 1;i++) s[i] = s[i - 1] + acnt[i];请问i为什么必须要小于N啊?等于N就错了。。

用户头像
玉米最强大   2021-03-14 21:07         踩      回复

前缀和写法里的

用户头像
AELee   2021-03-20 11:33    回复了 玉米最强大 的评论         踩      回复

越界

用户头像
zst_2001   2021-05-05 16:42         踩      回复

当你求 a 中小于 b 的个数时,是 as[i] = s[b[i] - 1] 对吧,但是数据范围中 值是可以取0的,这个时候的s[b[i]-1]中的b[i]可能会是0,那么0-1就越界了,这也是为什么赋值的时候需要给abc三个数组中的每个数都+1,既然加了1,那么一开始的数据范围就由 0 <= Ai,Bi,Ci <= N 变成了 1 <= Ai,Bi,Ci <= N + 1 ,这个时候你的ABC三个数组中就不存在值为0的数据了。
前缀和数组从1开始是为了 s[i - 1] 不越界,然后就是(这里的N是100010) <= N - 1 和 <= N 的问题,很明显 你数组开了N是 0~N-1,你怎么能 <= N 呢,换句话来说 我们这里 最大值也是 N + 1(数据范围的N是100000),但是我们的N一开始就是多开了 10个空间,这里N+1也不会影响到最终的结果。


用户头像
Kawasaki65   2021-01-11 17:09         踩      回复

怎么确保 i<j<k 呢

用户头像
小呆呆   2021-01-16 13:36         踩      回复

按照枚举顺序确保

用户头像
MapleSky   2022-01-06 20:39         踩      回复

题目说了要保证 i < j < k了吗?

用户头像
acyang   5天前         踩      回复

题目是1≤i,j,k≤N,没要求三个数的大小关系


用户头像
地球Online   2020-07-03 10:51         踩      回复

我想请教一下,为什么二分查找的两个mid的求法不一样呢?

用户头像
小呆呆   2020-07-04 19:35         踩      回复

一个是找最小的大于等于bi的元素位置,一个是找最大的小于等于bi的元素位置,具体二分模型去搜y总的二分模版

用户头像
地球Online   2020-07-04 20:25    回复了 小呆呆 的评论         踩      回复

好的,十分感谢!


用户头像
mzk   2020-05-04 16:33         踩      回复

我想问一下,这里二分的左右为什么是[0,n+1],不能是[1,n]

用户头像
小呆呆   2020-05-05 22:39         踩      回复

若不能找到则一定会在0或者n + 1的位置,否则就表示能找到,这样子可以区分是否能找到这样的对

用户头像
mzk   2020-05-06 08:28    回复了 小呆呆 的评论         踩      回复

谢谢,搞明白了


用户头像
bili   2020-01-22 08:11         踩      回复

我想请教一下什么时候用Buffer什么时候用Scanner呢?

用户头像
小呆呆   2020-01-22 11:46         踩      回复

如果输入的数据的数量达到1万以上的话用Buffer比较好,输入数据的数量比较小的话两个差不多

用户头像
bili   2020-01-30 15:23    回复了 小呆呆 的评论         踩      回复

我看有人用private static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));这个 那和System.out.println 有什么区别呢?

用户头像
小呆呆   2020-01-30 15:39    回复了 bili 的评论         踩      回复

通俗的来讲,如果输出很庞大的时候,用BufferWriter,这样会快很多,一般的输出用System.out.println就可以了,https://www.acwing.com/problem/content/175/你可以做一下这个题目,这个题目的输出就得用BufferedWriter,具体在哪个范围才用我也还没摸清楚hh,毕竟一般用System.out.println都能解决

用户头像
bili   2020-01-30 15:53    回复了 小呆呆 的评论         踩      回复

嗯嗯,好滴,谢谢啦


用户头像
JasonSun   2020-01-06 03:55         踩      回复

我推荐你封装一个 scanner类。 里面有 next_int, next_string 等等的helper 函数。 以后做题方便点。

用户头像
小呆呆   2020-01-06 10:02         踩      回复

输入输出的感觉还行,有时输入Scanner和Buffer还得看情况用,谢谢您的推荐。


你确定删除吗?
1024
x

© 2018-2023 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息