Day1 T3 数列

题目

给你一个长度为N的正整数序列,如果一个连续的子序列,子序列的和能够被K整除,那么就视此子序列合法,求原序列包括多少个合法的连续子序列?对于一个长度为8的序列,K=4的情况:2, 1, 2, 1, 1, 2, 1, 2 。它的答案为6,子序列是位置1->位置8,2->4,2->7,3->5,4->6,5->7。

输入

第一行:T,表示数据组数
对于每组数据:
第一行:2个数,K,N
第二行:N个数,表示这个序列

输出

共T行,每行一个数表示答案

样例

输入 输出
2
7 3
1 2 3
4 8
2 1 2 1 1 2 1 2
0
6

数据范围

100%数据满足1 (leqslant) T (leqslant) 20,1 (leqslant) N (leqslant) 50000,1 (leqslant) K (leqslant) 1000000,序列的每个数 (leqslant) 1000000000
30%数据满足1 (leqslant) T (leqslant) 10,1 (leqslant) N,K (leqslant) 1000

题解

先跑一遍前缀和,在前缀和的时候别忘了取模,一是会爆,二是后面有用。
如果一个从头开始的子序列的和与另一个从头开始的子序列的和同余,设其分别为(S_a)(S_b),不妨设(S_b)>(S_a)
(S_a≡S_b(mod k))
(0≡S_b-S_a(mod k))
又∵0对任何输取模都为0,
(S_b-S_a mod k=0)
即从a到b的子序列之和能被K整除。
然后统计模相等的从头开始的子序列即可。一对可以产生一个和能被K整除的子序列,n个便能排列出(frac{n(n-1)}{2})对(小学生都会的排列组合),可以产生(frac{n(n-1)}{2})能被K整除的子序列。
把sort用桶排序替换,时间复杂度可以降到O(n+k)。
其实就是我懒得写桶排

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rnd,k,n;
ll cnt=0;
int s[50010],a[50010];

int main(){
	scanf("%d",&rnd);
	while(rnd--){
		memset(a,0,sizeof(a));
		memset(s,0,sizeof(s));
		cnt=0;
		scanf("%d %d",&k,&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			a[i]%=k;
			s[i]=(s[i-1]+a[i])%k;
		}
		sort(s,s+n+1);
		int t=1; 
		for(int i=1;i<=n;i++){
			if(s[i]==s[i-1]) t++;
			else{
				cnt+=t*(t-1)/2;
				t=1;
			}
		}
		cnt+=t*(t-1)/2;
		//如果s[n]=s[n-1],便没有再运行else中的cnt+=t*(t-1)/2,所以在for循环之后还要再写一句 
		printf("%lld
",cnt);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/znk161223/p/11427754.html