问题 E: 求方案

题目描述
有n个正整数排成一行。你的目的是要从中取出一个或连续的若干个数,使它们的和能够被k整除。
例如,有6个正整数,它们依次为1,2,6,3,7,4。若k=3,则你可以取出1,2,6,或者2,6,3,7,也可以仅仅取出一个6或者3使你所取的数之和能被3整除。当然,满足要求的取法不止以上这4种。事实上,一共有7种取法满足要求。
给定n和k,以及这n个数。你的任务就是确定从这n个数中取出其中一个数或者若干连续的数,使它们的和能被k整除有多少方法。记Ha=1234567,由于取法可能很多,因此你只需要输出它mod Ha的值即可

输入
第一行为两个整数n,k。以下n行每行一个正整数,描述这个序列。

输出
输出一个整数,为答案mod Ha的结果。

样例输入
复制样例数据
6 3
1
2
6
3
7
4
样例输出
7

首先要求一段和是k的倍数, 那么首先想到的肯定是利用前缀和sum[i] ,求前缀和, 然后区间和就是sum[j] - sum[i - 1]. j , i ∈(1 , n) , 那么只要(sum[j] - sum[i - 1]) % k == 0 , 那么这个就是一个成功的方案, 那么这个公式又表示什么呢, 两个前缀和相减是k的倍数,不就是这两个前缀和同余嘛, 可以简单的化简一下,就出来了, 令sum[j] = ka + t1 , sum[i - 1] = kb + t2 , 那么为了满足这个公式就是k(a - b) + t1 - t2 是k的倍数, 此时此刻t1 + t2 == 0 , 即两个前缀和同余,
由此我们可以随便用两个sum % k 相等的数构成一个区间和,并且这个区间和肯定满足题目给的条件,那么我们就存一下sum % k 有多少个相同 , num[sum % k] ++ ,然后我们就可以枚举余数, 用该余数所代表的前缀和构成一个个区间和,
这个题也就转化为了, 从一个余数相同的集合里面拿出两个数来,相减构成一个新的数,有多少方案 , 第一有num[i] 个方案 , 第二次有num[i - 1] 个方案, 但是有重复的,所以就是num[i] * (num[i ] - 1) / 2, 注意,num表示的是前缀和%k的余数, 我们用两个这种数来构造,是构造一个区间和满足题目要求, 但是这个区间和要是就是一个前缀和, 我们之前在一个集合里拿出两个元素的方法就不适用了, 因为那个是两个数相减, 要是只是前缀和%k == 0 了, 也就是前缀和就满足要求了,此时此刻,就是ans + num[0] 了

#include <iostream>
using namespace std;
const int N = 5e5 +10 ;
typedef long long ll ;
ll a[N] ;
 
int main()
{
    int n , k ;
    cin >> n >> k ;
    ll t , sum = 0 ;
    for(int i = 1 ; i <= n ;i ++)
        cin >> t , sum += t , a[sum % k] ++ , sum %= k ;
    ll ans = 0 ;
     
    for(int i = 0 ;i <= k;i ++)
     ans += a[i] * (a[i] - 1) / 2 ;
    ans += a[0] ;
    cout << ans << endl ; 
    return 0 ;
}
posted @ 2019-10-11 22:05  spnooyseed  阅读(76)  评论(0编辑  收藏  举报