问题 E: k倍区间
题目描述
给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗
输入
每个数据包含多组输入数据
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出
输出一个整数,代表K倍区间的数目。
样例输入
5 2
1
2
3
4
5
样例输出
6
思路:所有输入值a[i]=(a[i]+a[i-1])%k,即得(1,i)区间对k的余数。
例如案例:
1 2 3 4 5 取余后为:
a[1]=1,a[2]=1,a[3]=0,a[4]=0,a[5]=1;
a[1]+a[2]%k=1,说明a[2]一定能被k整除;
a[3]=0,即((a[1]+a[2])%k+a[3])%k=0,说明a[1]到a[3]之和肯定能被k整除,即(1,3)肯定能构成k倍区间;
同理a[4]=0,说明a[1]到a[4]之和肯定能被k整除,即(1,4)肯定能构成k倍区间,并且上一个a[i]=0到a[4]=0存在一个k倍区间(i+1,4)即(4,4)(此时cnt[0]=2,ans+=cnt[0]);
a[5]=1,由于之前存在a[1]=1,a[2]=1,所以(2,5),(3,5)肯定能构成k倍区间(此时cnt[1]=2);
得出这样的规律:
1.a[i]!=0时
所有输入值经过a[i]=(a[i]+a[i-1])%k运算后,a[i]=x,当再次出现a[j]=x,a[k]=x时,(i+1,j),(i+1,k),(j+1,k)均为k倍区间,随着a[i]=x次数的增加,cnt[x]++(因为a[i]与之前的x值也可得到k倍区间)
(初始化cnt[x]=0)
2.a[i]==0,a[j]==0时
说明(1,i),(1,j),(i+1,j)均为k倍区间,且随着a[i]=0次数的增加,cnt[0]++(因为a[i]与之前的0值也可得到k倍区间)
(初始化cnt[0]=1)
/...菜鸡的一点学习成果,欢迎大佬们指正错误.../
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#define mod 998244353;
#define Max 0x3f3f3f3f;
#define Min 0xc0c0c0c0;
#define mst(a) memset(a,0,sizeof(a))
#define f(i,a,b) for(int i=a;i<b;i++)
using namespace std;
typedef long long ll;
const int maxn=100005;
int n,k;
int arr[maxn],cnt[maxn];//cnts数组记录每个余数的出现次数
int main(){
ios::sync_with_stdio(false);
while(cin>>n>>k){
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++){
cin>>arr[i];
arr[i]=(arr[i]+arr[i-1])%k;//记录(1,i)所有区间的对k取余值
}
cnt[0]=1;//第一次出现a[i]=0,(1,i)即为k倍区间
ll ans=0;
for(int i=1;i<=n;i++){
ans+=cnt[arr[i]]; //cnt[i]表示每个余数之前的出现次数
cnt[arr[i]]++; //之前出现过则可以构成k倍区间,用ans来累加方案数
}
cout<<ans<<endl;
}
return 0;
}