蓝桥杯2017真题10k倍区间数

10、k倍区间

1、题目

标题: k倍区间

给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。

你能求出数列中总共有多少个K倍区间吗?

输入
-----
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)

输出
-----
输出一个整数,代表K倍区间的数目。


例如,
输入:
5 2
1
2
3
4
5

程序应该输出:
6

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 2000ms

题目解析:区域片段求和问题,我们可以用前缀和,或者区段和,树状数组

本题连续区间和我们采用前缀数组,进行首次求解,最终用数学问题优化

  • 前缀解法
 public static void main(String[] args) throws FileNotFoundException {
//        System.setIn(new FileInputStream(new File("D:/Java_code/举起蓝桥杯/真题/data/in2.txt")));
        Scanner scanner = new Scanner(System.in);
        int nums = scanner.nextInt();
        int k = scanner.nextInt();
        int[] arr = new int[nums + 1];
        int[] sum = new int[nums + 1];
        for (int i = 1; i < nums + 1; i++) {
            arr[i] = scanner.nextInt();
            sum[i] = sum[i - 1] + arr[i];
        }
        int count = 0;
        for (int i = 1; i < nums + 1; i++) {
            for (int j = i; j < nums + 1; j++) {
                if ((sum[j] - sum[i - 1]) % k == 0) {
                    count++;
                }
            }
        }
        System.out.println(count);
    }

复杂度:100000 * 100000*100000,肯定超时,需要算很久

  • 但是这个方法要记住,前缀数组求连续区间和,使用一个数组sum【i】表示原数组前 i 的和,注意都要从 1 开始,不然不方便
  • 然后再两层循环遍历 sum数组,可以遍历出每个区间的和

优化:

  static int[] arr;
    static int[] sum;
    static Map<Integer, Long> map = new HashMap<>();
    public static void main(String[] args) throws FileNotFoundException {
        System.setIn(new FileInputStream(new File("D:/Java_code/举起蓝桥杯/真题/data/in7.txt")));
        Scanner scanner = new Scanner(System.in);
        int nums = scanner.nextInt();
        int k = scanner.nextInt();
        arr = new int[nums + 1];
        sum = new int[nums + 1];
        map.put(0, 1l);
        for (int i = 1; i < nums + 1; i++) {
            arr[i] = scanner.nextInt();
            sum[i] = (sum[i - 1] + arr[i]) % k;
            if (map.get(sum[i]) == null) {
                map.put(sum[i], 1l);
            } else {
                map.put(sum[i], map.get(sum[i]) + 1);
            }
        }
        long count = 0;
        for (int i = 0; i < k; i++) {
            Long cnt = map.get(i);
            if (cnt == null) {
                cnt = 0l;
            }
            count += (cnt*(cnt - 1)) >> 1;
        }
        System.out.println(count);
    }

解题思路

数学知识:如果两个数对同一个数取余相等,那么这两个数的差一定能整除那个数,也就是:

若 a%k = c && b%k = c 则 ( a - b)% k = 0;

这样就可以设计sum[i]为arr[i] 前i项取余的结果,再用map进行记录看相同的余数的连续数组和有多少个,

然后在 随便选两个就能整除 k,相当于数学中的组合 !

posted @ 2020-04-09 11:53  贝加尔湖畔╭  阅读(172)  评论(0编辑  收藏  举报