【截断区间】
- 介绍:给定长度为n的数组,将该数组从中间截断 , 得到三个非空子数组,要求这三个子数组内各元素之和相等
- 做法 : 一开始想用双指针来做,但是发现如果一个一个指两个指针的变化都非常大,所以用双指针不行,然后就开始会考虑前缀和,我们把这个数组的前缀和求出来,既然是需要三个区间,而且每个区间的和都是一样的,那么我们就要找到这个满足三分之一和三分之二和的位置在哪里
int solution(int q[] ,int sum , int n ){
int j = 0;
for(int i = 0 ; i < n - 1; i++){
//n - 1是因为指针不能指到头部否则无法形成一个区间
tot += q[i];
if( sum / 3 * 2 == tot){
ans += j ; //满足三分之二的区间加上前面满足三分之一和的区间的个数,也是一个组合
}
if(tot == sum / 3){
j ++;//找到了满足三分之一和的一个位置,区间个数加1
}
return ans;
}
【k倍区间】
-
描述:给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
-
你能求出数列中总共有多少个 K 倍区间吗?
-
想法: 如果想用双指针一个指向头部一个指向尾部,从最开始的元素开始求他的前缀和,然后遍历前缀和数组,找到能被k整除的个数,这么做确实很容易想到,但是这个题目的数据量是 1 0 5 10^5 105如果每次求完前缀和再减去头元素在遍历求k倍的前缀和,那么时间复杂度最大可能进行 1 0 10 10^{10} 1010,肯定是超时的,所以需要想一个更巧妙的办法
-
做法:比如说给的样例 : 1 2 3 4 5
-
我们求他的前缀和: 1 3 6 10 15
-
要求他的2倍区间,我们抛弃想法中的做法,我们可以吧前缀和的元素,都对k取余数那么我们会得到一个新的数组
-
新的数组为:1 1 0 0 1
-
既然和是k倍的,我们发现满足取余为0的都是符合条件的(比如说我们可以找到一个满足条件的区间[1,2,3])对于1的来说怎么看呢,满足1的都是除不断的,我们可以这么想,同时存在两个1会发生什么呢,是的如果说1号位置取余是1,2号位置取余是1,那会发生什么呢😋,对了,就是说2号位置是满足条件的。于此我们可以说,只要有两对或者两对以上的非零数存在,那么两个非0的且相同的数就可以构成一个符合条件的k倍区间。在这里我们有3个位置取余为1,分别是1号位置,2号位置,5号位置,我们可以怎么找呢,(1号 与 5号两个一凑我们就发现2号位置到5号位置所有元素的和是符合条件的,同理1,号和2号,我们找到2号位置自己,2号位置和5号位置我们找到3号位置到5号位置)
-
用一个更笨点的话去说,我告诉你现在1和2之间有n个符合条件区间,我现在也知道5号位置与1和2也可以构成一个符合条件的区间,其实就是最新的符合条件的1个加上之前存在的符合条件的个数(不断递加)
-
我们如何去找到这个递推公式呢,1号和2号产生了1个符合条件的区间,1 ,2 ,5,其实就是1和5组合2和5组合,用每次都加上上一次满足的个数递加
-
我们使用sum数组记录前缀和取模,res[i]就是取模之后i出现的次数代码一定仔细看
int solution(int sum[],int a[],int res[]){
long long ans = 0;
for(int i = 0 ; i <= n ; i++){
cin>>a[i];
sum[i] = (sum[i - 1] + a[i])%k;//前缀和的取模
ans += res[sum[i]];
res[sum[i]]++;
}
return ans - res[0] ;//res[0]肯定是符合条件的只要是取模为0都说明可以被k整除