题目链接
题意
给你一个区间,然后在这个找一个子区间,使得这个子区间的和 %
k
=
k=
k=(这个区间的数字个数)。
思路
一个很朴素的想法是预处理前缀和,然后
O
(
n
2
)
O(n^2)
O(n2)枚举,但是很遗憾 的是这里
n
n
n的范围太大了,这样的做法肯定T。
我们预处理的前缀和为
S
S
S
根据上面朴素算法的思想得
(
S
(
r
)
−
S
(
l
−
1
)
)
%
k
=
(
r
−
l
+
1
)
(\ S( r ) - S(l-1)\ )\%k=(r-l+1)
( S(r)−S(l−1) )%k=(r−l+1)
即
S
(
r
)
−
S
(
l
−
1
)
≡
r
−
l
+
1
(
m
o
d
k
)
S(r)-S(l-1)\equiv r-l+1(mod\ k)
S(r)−S(l−1)≡r−l+1(mod k)
即
S
(
r
)
−
S
(
l
−
1
)
=
r
−
l
+
1
+
k
∗
y
S(r)-S(l-1)=r-l+1+k*y
S(r)−S(l−1)=r−l+1+k∗y
即
(
S
(
r
)
−
r
)
%
k
=
(
S
(
l
−
1
)
−
(
l
−
1
)
)
%
k
(S(r)-r)\%k=(S(l-1)-(l-1))\%k
(S(r)−r)%k=(S(l−1)−(l−1))%k
所以我们只需要处理下前缀和,找到区间内满足上面条件的就行了,但是有
%
k
\%k
%k条件,所以区间长度要小于
m
m
m,这时我们设置个划窗就行。
这个题目非常要注意的一点是关于边界的处理,一不小心就可能卡很长时间。
首先,我们处理前缀和的范围是
0
到
n
0到n
0到n,然后划窗的长度为m,这时你可能有疑问,不是区间长度要小于m啊,应该设为m-1啊?
记住,你处理的是前缀和!!!
A
C
C
o
d
e
:
AC \ Code:
AC Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
const int N = 2e5+1000;
#define ll long long
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define gc(x) scanf(" %c",&x)
#define sread(x,y,z) scanf("%d%d%d",&x,&y,&z)
ll f[N];
map<ll,ll> hap;
int main()
{
int n,m;
Read(n,m);
for(int i = 1;i <= n;++i){
scanf("%lld",&f[i]);
f[i] += f[i-1];
}
for(int i = 1;i <= n;++i){
f[i] = (f[i] - i) % m;
}
m --;//与下面那句顺序不能反
m = min(m,n);
for(int i = 0;i < m;i ++ ){
hap[f[i]] ++;
}
ll sum = 0;
for(int i = m ;i <= n;++i){
hap[f[i]] ++;
sum += hap[f[i-m]] - 1;
hap[f[i-m]] --;
}
for(int i = n-m + 1;i <= n;++i){
sum += hap[f[i]] - 1;
hap[f[i]] --;
}
cout<<sum;
}