前缀和:K倍区间

前缀和:K倍区间

K 倍区间

www.acwing.com/problem/content/1232/

在这里插入图片描述

  • 任取一个 i i i 作为这个区间的右端点,那么就是求能令 S i − S i − k ≡ 0 S_{i}-S_{i-k}\equiv 0 SiSik0 的 $k $ 的个数 k ∈ [ 1 , i ] k\in [1, i] k[1,i] s [ r ] % k s[r] \% k s[r]%k s [ l − 1 ] % k s[l-1] \% k s[l1]%k 的余数如果相等,那么 s [ r ] − s [ l − 1 ] s[r] - s[l-1] s[r]s[l1] 的差值必然是 k 的倍数。

  • S i − S i − k ≡ 0 → S i ≡ S i − k   m o d   k S_{i}-S_{i-k}\equiv 0 \rightarrow S_{i} \equiv S_{i-k}\ mod \ k SiSik0SiSik mod k 即计算 S 0 , . . . S i − 1 S_0, ...S_{i-1} S0,...Si1 中与 S i S_i Si k k k 的余数相同的个数。需要统计所有满足 s [ j ] % K = = s [ i − 1 ] % K s[j] \% K == s[i-1] \% K s[j]%K==s[i1]%K ( i , j ) (i, j) (i,j) 对。

  • 创建哈希表 cnt[x]​,表示余数为 $x $ 的数的个数。假设 s[i] = 3,在后边的循环中,又出现了一个 s[i] = 3,那么此时,这个“3”可以和前边出现过的所有的“3”分别构成一个 K 倍区间,前边的“3”一共出现过 cnt[s[i]]​ 次,所以此时又新增了 cnt[s[i]]​ 个 K 倍区间。

  • 为什么初始化 cnt[0] = 1​?

    • cnt[mod]​ 记录的是余数为 mod​ 的前缀和的数量
    • 初始时,s[0] = 0​,且 0 % K = 0​,所以 cnt[0]​ 应该初始化为 1。
    • 如果不初始化 cnt[0] = 1​,则会漏掉所有 s[j] % K == 0​ 的区间(即 [1, j]​ 的区间)。
import java.util.*;

public class Main {
    static final int N = 100010;
    static int[] cnt = new int[N];
    static long[] s = new long[N];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            s[i] = sc.nextLong();
            s[i] += s[i - 1];
        }

		cnt[0] = 1;		// s[0]=0
        long res = 0;
        for (int i = 1; i <= n; i++) {
            int mod = (int) (s[i] % k);
            // 先加上与Si模k相同的数
            res += cnt[mod];
            // 再递增
            cnt[mod]++;
        }
        System.out.println(res);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值