题目链接:https://atcoder.jp/contests/abc158/tasks/abc158_e
题意:给你一个含 n n n个数位的数,问你他有多少个子序列可以被 p p p整除。
思路:再见这题之前,我们先来证明一个引例。
假设
x
1
x
2
x
3
x
4
x
5
x_1x_2x_3x_4x_5
x1x2x3x4x5 %
p
=
=
m
p == m
p==m &&
x
1
x
2
×
1
0
3
x_1x_2 \times 10^3
x1x2×103 %
p
=
=
m
p == m
p==m
那么
x
3
x
4
x
5
x_3x_4x_5
x3x4x5 %
p
=
=
0
p == 0
p==0.
证明:
x
1
x
2
x
3
x
4
x
5
x_1x_2x_3x_4x_5
x1x2x3x4x5 %
p
p
p = ((
x
1
x
2
×
1
0
3
x_1x_2 \times 10^3
x1x2×103) %
p
p
p + (
x
3
x
4
x
5
x_3x_4x_5
x3x4x5)%
p
p
p) %
p
p
p
因为
x
1
x
2
×
1
0
3
x_1x_2 \times 10^3
x1x2×103 %
p
=
=
m
p == m
p==m,那么
x
3
x
4
x
5
x_3x_4x_5
x3x4x5 %
p
=
=
0
p == 0
p==0.
既然如此,我们就可以把
s
[
]
s[]
s[]所有的前缀数
×
1
0
n
−
i
+
1
\times 10^{n-i+1}
×10n−i+1
%
p
\%p
%p的值全部用一个数组
c
n
t
[
]
cnt[]
cnt[]记录下来这样相同模式相同的任意取两个都可以找到一个满足条件的子序列.
PS:
- 我们这个方法不能处理 p = = 2 ∣ ∣ p = = 5 p == 2 || p == 5 p==2∣∣p==5的情况,所以要特判,但特判2或5就很简单,直接判断 s [ i ] s[i] s[i]能不能被2或者5整除就行啦.
- c n t [ 0 ] cnt[0] cnt[0]要赋初值为1.
那这一题就结束啦。
AC代码:
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define pii pair<int,int>
#define sd(x) scanf("%d",&x)
#define slld(x) scanf("%lld",&x)
#define pd(x) printf("%d\n",x)
#define plld(x) printf("%lld\n",x)
#define rep(i,a,b) for(int i = (a) ; i <= (b) ; i++)
#define per(i,a,b) for(int i = (a) ; i >= (b) ; i--)
#define mem(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define fast_io ios::sync_with_stdio(false)
const int INF = 1e9;
const LL mod = 1e9 + 7;
const int maxn = 2e5 + 7;
char s[maxn];
LL cnt[maxn];
LL qpow(LL a,LL b,LL p) {
LL res = 1;
while(b) {
if(b & 1) res = (res * a) % p;
a = (a * a) % p;
b >>= 1;
}
return res%p;
}
int main() {
int n,p;
while(~scanf("%d%d",&n,&p)) {
scanf("%s",s);
int len = strlen(s);
LL ans = 0;
if(p == 2 || p == 5) {
rep(i,0,len-1) {
if((s[i] - '0') % p == 0) ans += 1LL * (i + 1);
}
} else {
mem(cnt);
LL sum = 0;
cnt[0] = 1;
rep(i,0,len-1) {
sum = (sum * 10 + (s[i] - '0')) % p;
LL val = (qpow(10LL,n-i-1,p) * (sum % p)) % p;
cnt[val]++;
}
rep(i,0,p-1) {
ans += cnt[i] * (cnt[i] - 1) / 2;
}
}
plld(ans);
}
return 0;
}