标题: 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
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
ps:这题如果直接枚举各个起点和终点的话肯定会TLE的,因此需要想办法将复杂度降低。
我们用数组
sum[i]
s
u
m
[
i
]
表示数组
data[0]+data[1]+...+data[i]
d
a
t
a
[
0
]
+
d
a
t
a
[
1
]
+
.
.
.
+
d
a
t
a
[
i
]
的和。那么区间
[i,j]
[
i
,
j
]
的和为
sum[j]−sum[i−1]
s
u
m
[
j
]
−
s
u
m
[
i
−
1
]
。如果区间
[i,j]
[
i
,
j
]
是
k
k
的倍数,有,由同余定理 ( 给定一个正整数m,如果两个整数a和b满足a - b能够被m整除,即
(a−b)/m
(
a
−
b
)
/
m
得到一个整数,那么就称整数a与b对模m同余,记作
a≡b (mod m)
a
≡
b
(
m
o
d
m
)
), 得
sum[j]%k==sum[i−1]%k
s
u
m
[
j
]
%
k
==
s
u
m
[
i
−
1
]
%
k
。 那么接下来我们用一个数组cnt[i]来储存sum数组取模 k 之后所有等于i的个数。最后用组合公式就可以得到最终的结果了。
下图是根据样例做出的解题过程和解析
为什么结果是6呢?我们可以简单地枚举一下这6种情况:
我们可以从
cnt[0]=2
c
n
t
[
0
]
=
2
和
cnt[1]=3
c
n
t
[
1
]
=
3
来得到上面的这几种情况,
cnt[0]=2
c
n
t
[
0
]
=
2
来自
sum[2]%2
s
u
m
[
2
]
%
2
和
sum[3]%3
s
u
m
[
3
]
%
3
,由上方的结论如果
sum[i]% k==sum[j]% k
s
u
m
[
i
]
%
k
==
s
u
m
[
j
]
%
k
, 则区间
[i+1,j]
[
i
+
1
,
j
]
是k的倍数。所以
sum[2]%2
s
u
m
[
2
]
%
2
和
sum[3]%3
s
u
m
[
3
]
%
3
可以推得区间
[3,3]
[
3
,
3
]
是k的倍数,同理根据其他的数据我们可以推出区间
[0,2],[2,4],[0,3],[1,4]
[
0
,
2
]
,
[
2
,
4
]
,
[
0
,
3
]
,
[
1
,
4
]
是k的倍数。但是cnt[0]比较特殊为什么呢?因为
cnt[0]=cnt[sum[i]%k=0]
c
n
t
[
0
]
=
c
n
t
[
s
u
m
[
i
]
%
k
=
0
]
,它从起始位置到i 所构成的区间本身就是k倍区间,因此cnt[0]要加上1,参与组合。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 1e5+10;
int data[maxn];
long long sum[maxn];
long long cnt[maxn];
int main()
{
//freopen("input.txt", "r", stdin);
ios::sync_with_stdio(false);
int n, k;
while(cin>>n>>k)
{
memset(sum, 0, sizeof sum);
memset(data, 0, sizeof data);
cin>>data[0];
sum[0] = data[0] % k;
for(int i = 1; i < n; i++) {
cin>>data[i];
sum[i] = (sum[i - 1] + data[i]) % k;//前缀和
}
cnt[0]++;
for(int i = 0; i < n; i++)
{//进行统计
cnt[sum[i]]++;
}
long long ans = 0;
for(int i = 0; i < n; i++)
{
ans += (1 + (cnt[i] - 1))*(cnt[i] - 1) / 2;//组合公式
}
cout<<ans<<endl;
}
return 0;
}
附上该题目的测试数据(打开方式建议用写字板,避免出现乱码), (测试数据来源于网络)(密码:g8ui)