【数论-整除】中山纪念中学暑期游Day1——数列

前言

巨水水水水的一道题,居然没做起,30分骗分完全在讽刺当时的自己好吗qwq...

题目

给你一个长度为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。

Input

第一行:T,表示数据组数

对于每组数据:

第一行:2个数,K,N

第二行:N个数,表示这个序列

Output

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

Sample Input

2 
7 3 
1 2 3 
4 8 
2 1 2 1 1 2 1 2 

Sample Output

0 
6 

数据范围

100%数据满足

1<=T<=20

1<=N<=50000

1<=K<=1000000

序列的每个数<=1000000000

30%数据满足

1<=T<=10

1<=N,K<=1000

分析

前缀和+“余数”思想+排列组合

1)预处理前缀和and各前缀和mod k 的个数

2)余数相同的前缀和数列相减得出——余数为0的子序列

3)排列组合一下:余数为 i 的前缀和数列个数设为cnt[ i ],它们两两组合可以有cnt[ i ]*( cnt[ i ]-1 )种情况,答案cnt[ 0 ]~cnt[ k-1 ]累加即可

有一个细节是cnt[ 0 ] (mod k 余数为0的前缀和数列个数)最后要单独加一次,或者其初值附为1,因为它自己就可以整除,不需要和别的前缀和数列相减

骗分代码(贴过来皮一下)

//已确认会超时orz... 
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=50000;
ll a[MAXN+5],pre[MAXN+5];
int k,n;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int ans=0;
		//int tot=0;
		scanf("%d%d",&k,&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			pre[i]=pre[i-1]+a[i];
		}	
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n-i+1;j++)//子序列开头位置
			{
				ll sum=pre[j+i-1]-pre[j-1];
				if(sum%(ll)k==0)
					ans++;
			}
		printf("%d\n",ans);
	}
	return 0;
}

AC代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=5e4,MAXK=1e6;
ll a[MAXN+5],pre[MAXN+5],cnt[MAXK+5];
int k,n,ans;
void Init()
{
	ans=0;
	memset(cnt,0,sizeof(cnt));
	cnt[0]=1;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		Init();
		scanf("%d%d",&k,&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			pre[i]=pre[i-1]+a[i];
			cnt[pre[i]%k]++;//计算余数个数 
		}
		for(int i=0;i<=k-1;i++)
			if(cnt[i]>=1)
				ans+=cnt[i]*(cnt[i]-1);
		printf("%d\n",ans/2);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值