1. 程式人生 > >SDUT3173 有多少個連續子序列的和能被k整除

SDUT3173 有多少個連續子序列的和能被k整除

題目描述

Edward 得到了一個長度為 N 的整數序列,他想找出這裡面有多少個“幸運的”連續子序列。一個連續子序列被稱為“幸運的”,當且僅當該子序列內的整數之和恰好是 K 的整數倍數。他請求你寫一個程式來計算他喜歡的連續子序列個數。

輸入

 輸入第一行是一個整數 T,表示有 T 組資料。 每組資料第一行是兩個整數 N (1 <= N <= 10^6 ), K (1 <= K <= 10^9 )。 接下來的一行包含 N 個整數 A i (|A i | <= 10^9 )。

輸出

 對於每組測試資料,輸出一行僅包含一個整數,表示 Edward 喜歡的連續子序列數量。

示例輸入

2
5 3
1 2 3 4 1
6 2
1 2 1 2 1 2

示例輸出

4
9
範圍很大不能用O(n2)的做法。這時考慮字首和同模做差。但是很難想到。
比如,有a b c d e,(a+b)%mod=k,(a+b+c+d+e)%mod也=k,那麼(c+d+e)%mod=0,即該子序列是mod的整數倍數。
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<string.h>
#include<stdlib.h>
#include<cstdio>
#define ll long long
using namespace std;
ll x[1000001];
ll C(ll m,ll n){
	ll i,j,sum=1;
	for (i=m,j=0;j<n;j++,i--)
		sum=sum*i/(j+1);
	return sum;
}
int main(){
	ll t,n,m;
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&m);
		x[0]=0;
		ll cnt=0;
		ll w;
		for(ll i=1;i<=n;++i){
			cin>>w;
			x[i]=((x[i-1]+w)%m+m)%m; //因為w有可能是負的 
		}
		sort(x,x+n+1);
		ll s=1;
		for(ll i=1;i<=n;++i){
			if(x[i]==x[i-1])
				s++;
			else{
				cnt+=C(s,2);
			    s=1;
			}
		}
		cnt+=C(s,2);
		printf("%lld\n",cnt);
	}
	return 0;
}