美团2017CodeM初赛A轮题解[E题]

数列互质

题意:给出一个长度为 n 的数列 { a[1] , a[2] , a[3] , ... , a[n] },以及 m 组询问 ( l[i] , r[i], k[i])。

求数列下标区间在 [ l[i] , r[i] ] 中有多少数在该区间中的出现次数与 k[i] 互质(最大公约数为1)。

输入描述:

第一行,两个正整数 n , m (1 ≤ n, m ≤ 50000)。
第二行,n 个正整数 a[i] (1 ≤ a[i] ≤ n)描述这个数列。
接下来 m 行,每行三个正整数 l[i] , r[i] , k[i] (1 ≤ l[i] ≤ r[i] ≤ n, 1 ≤ k[i] ≤ n),描述一次询问。

输出描述:

输出 m 行,即每次询问的答案。

分析:这道题要求的是在某个区间内有多少个数出现的次数与k[i]互质。

题目中提到的一个很关键的点是a[i]属于[1,n],可以根据这个构建一个不算大的数组来记录a[i]在[left,right]中出现的次数,再次遍历这个数组,即可求在这个区间中与相应的k互质的次数有多少个。

代码:

package CodeM;

import java.util.Scanner;

public class RelativelyPrime_2017Aans {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[] a = new int[n + 1];
        int maxid = -1;
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt();
            maxid = maxid > a[i] ? maxid : a[i];
        }
        for (int i = 1; i <= m; i++) {
            int[] vis = new int[maxid + 1];
            int l = sc.nextInt();
            int r = sc.nextInt();
            int k = sc.nextInt();
            for (int j = l; j <= r; j++) {
                vis[a[j]] += 1;
            }
            int res = 0;
            for (int p = 0; p < vis.length; p++) {
                if (vis[p] == 0) continue;
                if (gcd(vis[p], k) == 1) res += 1;
            }
            System.out.println(res);
        }
    }

    private static int gcd(int a, int b) {
        while (b > 0) {
            int t = a % b;
            a = b;
            b = t;
        }
        return a;
    }
}

还有一个解法是用一个二维数组times将每个数在当前位置为止出现的次数,在求[left,right]该数出现的次数时,就可以直接利用times[right]-times[left-1]求得。

import java.util.HashMap;
import java.util.Scanner;

public class RelativelyPrime_2017A {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[] a = new int[n + 1];
        HashMap<Integer, Integer> all = new HashMap<>();
        int k = 0;
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextInt();
            if (!all.containsKey(a[i])) {
                all.put(a[i], k++);
            }
        }
        //use times to record every a[i]'s times
        int[][] times = new int[all.size()][n + 1];
        for (int j = 1; j <= n; j++) {
            int key = all.get(a[j]);
            times[key][j] = times[key][j - 1] + 1;
            for (int i = 0; i < times.length; i++) {
                times[i][j] = Math.max(times[i][j], times[i][j - 1]);
            }
        }

        for (int i = 0; i < m; i++) {
            int l = sc.nextInt();
            int r = sc.nextInt();
            k = sc.nextInt();
            //ArrayList<Integer> exist = new ArrayList<>();
            long res = 0;
            for (int x = 0; x < times.length; x++) {
                int time = times[x][r] - times[x][l - 1];
                //System.out.println(l+"\t"+r+"\t"+time);
                if (time == 0)
                    continue;
                if (gcd(k, time) == 1) {
                    // exist.add(time);
                    res += 1;
                }
            }
            System.out.println(res);
        }
    }

    private static int gcd(int a, int b) {
        while (b > 0) {
            int t = a % b;
            a = b;
            b = t;
        }
        return a;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值