题意: 给定一个只包含数字字符且不包含     0字符的字符串     S,问有子串组成的十位数是     2019的倍数。
 数据范围:     ∣S∣≤2000000
 本题是     Atcoder Beginner Contest 158E的弱化版,故以     158E来证明。
题解:(参考题解)
 对于一个数形如     abcdefgh设为     AM,另一个数形如     efgh设为     M,     p为     10000内的质数
 若     AM%p==M%p即两者同余,可以得知     A×10len(M)%p=0
 证明即移项,根据取模的性质:     (a−b)%p=(a%p−b%p)%p,故得证。
下面来讨论符合的情况:
 设     B=10len(M),那么     A×B%p=0则有:
      1.A%p=0
      2.B%p=0
      3.由于模的性质,那么     A×B%p=((A%p)×(B%p))%p=0,但是两个小于     p的数的乘积是不会存在     p这个质因子的,故该情况不符合,直接     over
当第 2种情况成立时,由于 p是质因子, 10可分解为 5与 2的乘积,故任何符合条件的区间的个位必是 5或 2的倍数,即 2,4,6,8,0,5
当第二种情况不成立即     p=2&&p=5,那么所有     B%p相等的情况互相可以组成一种。
 即     B1和     B2的后缀模数相等时,如上的      abcd部分是符合要求的区间。
代码:
158E
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
char s[N];
int cnt[10010];
int n, p;
int main()
{
	scanf("%d%d%s", &n, &p, s + 1);
	if(p == 2 || p == 5) {
		ll res = 0;
		for(int i = 1; i <= n; i++) {
			int v = s[i] - '0';
			if(v % p == 0) res += i;
		}
		
		return 0 * printf("%lld\n", res);
	}
	
	cnt[0] = 1;
	ll j = 1, now = 0, res = 0;
	for(int i = n; i >= 1; i--) {
		int v = s[i] - '0';
		now = (now + j * v) % p;
		res += cnt[now];
		cnt[now]++;
		j = j * 10 % p;
	}
	
	printf("%lld\n", res);
	
	return 0;
}
  164D
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
const int p = 2019;
char s[N];
int cnt[2020];
int main()
{
	scanf("%s", s + 1);
	
	cnt[0] = 1;
	ll j = 1, now = 0, res = 0;
	for(int i = strlen(s + 1); i >= 1; i--) {
		int v = s[i] - '0';
		now = (now + j * v) % p;
		res += cnt[now];
		cnt[now]++;
		j = j * 10 % p;
	}
	
	printf("%lld\n", res);
	
	return 0;
}

京公网安备 11010502036488号