ABC164 D.Multiple of 2019

4 篇文章 0 订阅
1 篇文章 0 订阅

D.Multiple of 2019

Question

给一个字符串S,求有多少个子串在十进制下为2019的倍数。
在这里插入图片描述

Solution

前置知识:
S [ l ] [ r ] × 1 0 l − r = s [ l ] [ k ] − s [ r ] [ k ] S[l][r]\times10^{l-r}=s[l][k]-s[r][k] S[l][r]×10lr=s[l][k]s[r][k]
S [ l ] [ k ] − S [ r ] [ k ] ≡ 0 ( m o d   P ) S[l][k]-S[r][k] \equiv 0(mod\ P) S[l][k]S[r][k]0(mod P)
∵ 1 0 x m o d   P ≠ 0 ∵10^{x} mod\ P \neq 0 10xmod P=0
∴ S [ l ] [ r ]   m o d   P = 0 ∴S[l][r]\ mod \ P =0 S[l][r] mod P=0
S [ l ] [ r ] ≡ 0 ( m o d   P ) S[l][r] \equiv 0(mod\ P) S[l][r]0(mod P)
S [ l ] [ r ] × 1 0 r − l   m o d   P = 0 S[l][r] \times 10^{r-l} \ mod \ P = 0 S[l][r]×10rl mod P=0

这道题再增加难度就是任意模数 P P P了二不一定是 2019 2019 2019,上面证明了任意模数P非10的因子的情况下。
下面讲如何处理 P P P 10 10 10的因子情况下 : : :
1 1 1 n n n遍历若一个数个位为 10 10 10的因子,则前面的数肯定是 10 10 10的倍数,所以前面的数被 P P P取余一定为 0 0 0
对应的ABC158E

将题意转化为对2019求余为0的子串有多少个。求一个后缀模数数组,后缀数组取模后的数为 i i i,后缀数组取模后为 i i i的个数为 M [ i ] M[i] M[i]
i 的 贡 献 值 = M [ i ] ∗ ( M [ i ] − 1 ) / 2 i的贡献值=M[i]*(M[i]-1)/2 i=M[i](M[i]1)/2这里可以转化为加法,加上之前已有的 M [ i ] M[i] M[i]之后再更新 M [ i ] M[i] M[i],注意初始化 M [ 0 ] = 1 M[0]=1 M[0]=1

Code ABC164D

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const double eps = 1e-8;
const int NINF = 0xc0c0c0c0;
const int INF  = 0x3f3f3f3f;
const ll  mod  = 2019;
const ll  maxn = 1e6 + 5;
const int N = 2e5 + 5;

char s[N];
int a[N],suf[N],n,M[2020];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>s+1;
	int n=strlen(s+1);
	int cnt=0;int base=1;
	M[0]++;
	for(int i=n;i>=1;i--){
		a[i]=s[i]-'0';
		suf[i]=(suf[i+1]+a[i]*base)%mod;
		cnt+=M[suf[i]];
		M[suf[i]]++;
		base=base*10%mod;
	}
	cout<<cnt<<'\n';
	return 0;
}

Code2 ABC158E

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const double eps = 1e-8;
const int NINF = 0xc0c0c0c0;
const int INF  = 0x3f3f3f3f;
const ll  mod  = 1e9 + 7;
const ll  maxn = 1e6 + 5;
const int N = 2e5 + 5;

char s[N];
int suf[N],cnt[N];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n,p;cin>>n>>p>>s+1;
	int base=1;
	ll ans=0;
	if(10%p==0){
		for(int i=1;i<=n;i++){
			if((s[i]-'0')%p==0) ans+=i;
		}
	}else{
		cnt[0]++;
		for(int i=n;i>=1;i--){
			int j=s[i]-'0';
			suf[i]=(suf[i+1]+j*base)%p;
			ans+=cnt[suf[i]];
			cnt[suf[i]]++;
			base=base*10%p;
		}
	}
	cout<<ans<<'\n';
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值