组合数学二
时间限制: 1 Sec 内存限制: 64 MB题目描述
有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整除有多少方法。 由于取法可能很多,因此你只需要输出它mod 1234567的值即可。
输入
第一行有两个正整数,分别代表n和k。输入数据保证有n小于等于500 000,k小于等于100 000。 以下n行每行一个正整数。这些正整数保证都不大于10 000。
输出
一个正整数。它应该是你的答案mod 1234567的结果。
样例输入
6 3
1
2
6
3
7
4
样例输出
7
求一段连续位置上数字和,对数列求前缀和sum,从第j + 1项到第i项的和:sum[ i ] - sum[ j ]
则 ( sum[ i ] - sum[ j ] ) % k = 0
-> sum[ i ] ≡ sum[ j ] (mod k)且 i > j-> i >= j + 1(i = j + 1时是单独的一个数)
将sum分类,可以用k的剩余系来表示sum中的一类数,Z(k) = {0 , 1 , 2 , .... , k - 1}
对于每类数,由于i > j,有:(当余数为k1时,共有n个数余数为k1)
i = a1, j = a2,a3,a4,....,an(n - 1)组
i = a2 , j = a3, a4,....,an(n - 2)组
....
i = a(n-1) , j = an(1)组
所以对于余数k1,一共有(n - 1) + (n - 2) + ....+1 = (n - 1)*n / 2
当k1 = 0时, 不仅两个数一组, 它每个数本身也作为一种方案,共n种
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef unsigned long long LL;
const int mod=1234567;
int R[100005];
int main(){
int N,K,x;
LL ans=0,sum=0;
scanf("%d%d",&N,&K);
for(int i=1; i<=N; ++i){
scanf("%d",&x);
sum+=x%K;
++R[sum%K];
}
ans=R[0]%mod;
for(int i=0; i<K; ++i)
ans=(ans+(R[i]*(R[i]-1)>>1))%mod;
cout<<ans<<endl;
}