看到连续子序列,而且这道题的严格的控制了时间,我们应该想到前缀和(不懂的小伙伴可以参考其他优秀博客前缀和与差分_c语言差分和前缀和-CSDN博客),假设我们按照题意构建好了一个前缀和数组。
接下来我们采取这样的思路:利用数学知识,我们知道,假设数字a对某个数s取模的值等于数字b对s取模的值,那么a-b就可以被s整除(假设a>b),既然如此,我们不妨让前缀和数组的每一项都对k取模,让每一项保存各自取模后的结果,这样的话,但凡数组用存在两个元素(比如prefix[i]==prefix[j])相等,那么[i,j]就是数列的一个k倍区间啦! (大家细细品味)
AC代码:
#include<stdio.h>
#define ll long long
ll a[100002],b[100002];
ll result;
int main(){
ll n,k,cur;
scanf("%lld%lld",&n,&k);
ll i;
for(i=1;i<=n;i++){
scanf("%lld",&cur);
a[i]=a[i-1]+cur;//构建前缀和数组
}
for(i=0;i<=n;i++){
result+=b[a[i]%k];
b[a[i]%k]++;
}
printf("%lld",result);
return 0;
}
我们一般把前缀和数组的第一项赋值为0.这里的数组b是记录前缀和数组中每个数字出现的次数,比如:用样例 得到的前缀和数组是{0 ,1,3,6,10,15}经过取模后变成{0,1,1,0,0,1}
比如数字0出现了3次,b[3]就等于3 。
for(i=0;i<=n;i++){
result+=b[a[i]%k];
b[a[i]%k]++;
}
这里可能会比较难理解,这段代码执行的功能就是把取模后数组的相同元素配对,但是不是很常规。按照一般的思路,数组里面有2个1,配对的方式只要一种,有三个0,配对的方式有这么3种。
有小伙伴可能会问,这个最初的0是我们自己赋值的,这样算会不会影响结果?其实,这正是把前缀和数组头元素置0的巧妙之处!因为如果前缀和数组的某个元素恰好能被k整除(对k取模为0)时,这就说明从开头元素到这个元素是k倍子区间。这种特殊的k倍子区间恰好可以由开头我们置的0和前缀和数组里面其他的0配对而成!可以帮助我们省去麻烦的分类讨论!!
按一般的思路来说,应该先把b[i]算出来,然后有b[i]!/2种配对方式,但计算阶乘可能会超时,上面的代码同样可以实现计算配对方式数目,大家可以自行模拟体会,在今后可以利用这种方式计算配对数目!
蟹蟹大家!