Multiple of 2019
传送门
题意:给一个巨长的数列,要求找出是2019的倍数的片段数目。
思路:
我就知道是前缀和和同余.…但是菜鸡如我只想出了O(n^2)的方法。一直纠结在这个10怎么办,TLE还觉得是输入有问题
正解的思路:
开一个数组
p
r
e
f
[
i
]
pref[i]
pref[i],记录:
a
n
+
1
0
1
×
a
n
−
1
+
1
0
2
×
a
n
−
2
+
.
.
.
+
1
0
n
−
i
×
a
i
a_n+10^1\times a_{n-1}+10^2\times a_{n-2}+...+10^{n-i}\times a_i
an+101×an−1+102×an−2+...+10n−i×ai。这样一来,
(
i
,
j
)
(i,j)
(i,j)之间的数字就是
(
p
r
e
f
[
i
]
−
p
r
e
f
[
j
+
1
]
)
÷
1
0
n
−
j
(pref[i]-pref[j+1])\div 10^{n-j}
(pref[i]−pref[j+1])÷10n−j。要使得
(
i
,
j
)
m
o
d
2019
=
0
(i,j)mod2019=0
(i,j)mod2019=0,只需要
(
p
r
e
f
[
i
]
−
p
r
e
f
[
j
+
1
]
)
m
o
d
2019
=
0
(pref[i]-pref[j+1])mod2019=0
(pref[i]−pref[j+1])mod2019=0(因为10的因子都不是2019的因子,所以不考虑外面的10) 即
p
r
e
f
[
i
]
pref[i]
pref[i]和
p
r
e
f
[
j
+
1
]
pref[j+1]
pref[j+1]同余。那么只用计算同余的组合数就可以了。
怎么得到
p
r
e
f
[
i
]
pref[i]
pref[i]数组呢?只需要后一个
p
r
e
f
[
i
+
1
]
pref[i+1]
pref[i+1]加上
1
0
n
−
i
×
s
[
i
]
10^{n-i}\times s[i]
10n−i×s[i]即可。
那怎么计算组合数呢? 因为组合数的公式为
n
×
(
n
−
1
)
2
\frac{n\times (n-1)}{2}
2n×(n−1),正好等于等差数列
1
+
2
+
.
.
.
+
(
n
−
1
)
1+2+...+(n-1)
1+2+...+(n−1)的和。所以就可以线性计算了。(见代码)
还要注意,如果计算
p
r
e
f
[
i
]
pref[i]
pref[i]的时候出现了
0
0
0,那他自己就可以整除。每出现一次这样的情况,组合数就相当于加上了现存的
c
n
t
[
0
]
cnt[0]
cnt[0],等价于一开始就初始化为1.
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=200004;
int rmd[maxn];//记录remainder
int pref[maxn];
char s[maxn];
int main(){
scanf("%s",s+1);
int base=1;
long long cnt=0;
rmd[0]=1;//注意
int len=strlen(s+1);
for(int i=len;i>=1;i--){
pref[i]=(pref[i+1]+base*(s[i]-'0'))%2019;
base=base*10%2019;
cnt+=rmd[pref[i]];//计算组合数
rmd[pref[i]]++;
}
printf("%lld\n",cnt);
}
下面是令人窒息的菜鸡TLE操作(((
#include<bits/stdc++.h>
using namespace std;
int mod[200004];
int ten[200004];
int main(){
int len=0;
ten[0]=1;
char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) {
int tmp=ch-'0';
ten[++len]=10*ten[len-1]%2019;
mod[len]=(tmp%2019+mod[len-1]*10%2019)%2019;
ch = getchar();
}
long long ans=0;
for(int i=1;i<=len;i++){
for(int j=i+1;j<=len;j++){
if((mod[j]+2019-mod[i-1]*ten[j-i+1])%2019==0){
ans++;
}
}
}
printf("%lld\n",ans);
}