1.实时读取数据
cin >> son[i];
2.获取sum[i]
if (i != 0) sum[i] = (sum[i - 1] + son[i]) % k; else sum[i] = son[i] % k;
sum[i]我们都能理解,就是累加和嘛,可这里偏偏要%k是什么鬼意思?这里要当心了,一步看不懂,后面的全都看不懂了,我们把sum当做一个存储累加和余数的数组,我再强调一遍,“累加和余数”!!!!
所以:
当i == 0时,sum[i] = son[i] % k,即sum[0] = son[0] % k;
当i == 1时,sum[i] = (sum[i - 1] + son[i])% k,即sum[1] = (sum[0] + son[1]) % k;
记住我说的,sum是余数和,后面你就忘掉那个“和”字,只记住“余数”,然后来展开下上面的公式:
sum[1] = (sum[0] + son[1]) % k; sum[1] = sum[0] % k + son[1] % k; sum[1] = sum[0] + son[1] % k; //sum[0]已经是余数了,所以%k没有意义,是本身
第一种解法,sumArray存储的是每一项从0开始的当前项的累加和;
这里sum存储的就是sumArray存储的是每一项从0开始的当前项余数的累加和;
3.先说sum[i] == 0的情况
1. if (sum[i] == 0) ans++;
这个很明显,只要整数取余为0,那必然是符合k区间条件的,这里ans++应该不难理解,因为sum中存储的全是余数,必然不会全是0,有人会有疑惑,假如有多个不为0的余数存在,那么sum[i] == 0这个条件还能成立吗?答案是肯定的,这就是为什么余数也要进行相加的原因:
sum[i] = (sum[i - 1] + son[i]) % k;
余数(被忽略的部分必然被整除了)+余数(被忽略的部分必然被整除了)能被整除就是0,不能被整除就是另一个余数,也有可能和当前余数相等,相等怎么办?看下面:
4.区间计算的重头戏
b[sum[i]]++; ans += b[sum[i]] - 1;
能不能看得懂就全看这里了!!!
b是什么?
b是用来存储相等的余数出现的次数的!!!
如果余数相等,则对应的i,j相减就把余数减去了,剩下的必定可以整除,这就是一个新的区间!!!
我们从0开始模拟下b:
假设余数为3:
余数3出现1次时:
b[3]++ = 1,说明余数为3的累加和出现1次,这时候没有成对出现,无法用j-i判断是否存在k倍区间;
余数3出现2次时:
b[3]++ = 2,说明余数为3的累加和出现2次,这时候存在1个k倍区间,为什么是1个呢?假设这2个累加和下表分别是i,j,那么有,j-i这个一个区间;
余数3出现3次时:
b[3]++ = 3,说明余数为3的累加和出现3次,这时候存在2个k倍区间,为什么是2个呢?假设这3个累加和下表分别是i,j,k,那么有,k-i,k-j,有人说,不是还有个j-i吗?没错,但是j-i已经在余数出现第2次的时候存在了,不能计算在内;
余数3出现4次时:
b[3]++ = 4,说明余数为3的累加和出现4次,这时候存在3个k倍区间,为什么是3个呢?假设这4个累加和下表分别是i,j,k,l那么有,l-i,l-j,l-k有人说,不是还有个j-i和k-i,k-j吗?没错!你抬头看看余数出现第2,3次的里面有没有没列出来的这三个?所以不能计算在内;
所以,你发现规律了吗?像不像选择排序法里面那样?用最后一个和前面的所有项比较,这还真是选择排序法里德运用。
ans += b[sum[i]] - 1;
这时候,你已经明白了b[sum[i]] 的含义,你可能还需要再消化下......
已经看懂的同学我们继续下一个问题,为什么要减1?
一个余数的时候,区间不成立;
两个相等余数的时候,两两比较,只能有1个;
三个相等余数的时候,最后一位和前两个比较,只有2个,
......
n个相等余数的时候,最后一位和前n-1个比较,只有n-1个.
因为始终有一个要作为参照系,所以只能存在n-1个,上面的举例已经清楚说明了原因。
总结
到这里,今天的学习就要结束了,说实话,这个题真TM难,就这,还被列入了简单之类,😂但是你要是听懂了,你会发现其实也不难,就是一个理念问题,可问题是,这样的理念不是一般人想的出来的。
另外,博主在查找的时候看到有些人的解析里面用到了c(m,n),这是什么呢?
从n个不同的元素中,任取m(m≤n)个元素为一组,叫作从n个不同元素中取出m个元素的一个组合。所以它叫排列组合,记得大学里面高数里讲概率的时候有讲过,相信很多人都忘了。根据这种理论,会有一种新的写法,但思路和第二种是一样的,我们是直接通过从n个余数中找出有几种组合的方式,c(m,n)是根据数学的思维通过公式计算sumArray中存在余数的组合方式:
1 2 3 4 5
son[]
1 2 3 4 5
1 0 1 0 1 每一项整除取余
这里是2个
sum[]
1 3 6 10 15
1 1 0 0 1 累加和整除取余
这里1是c(3,2)=3;
这里0是c(2,2)=1;
2 + 3 + 1 = 6
感兴趣的可以去了解下这种方式,不再赘述,有问题欢迎评论区留言讨论。