1 条题解
-
0
自动搬运
来自洛谷,原作者为

yizhishenjing
深鲸搬运于
2025-08-24 22:35:13,当前版本为作者最后更新于2025-07-22 16:34:10,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目描述
给定一个长度为 的数字串 和三个质数 ,,,需要将数字串划分为三段非空子串,使得:
- 第一段能被 整除。
- 第二段能被 整除。
- 第三段能被 整除。
- 每一段不含前导零(单独的 是允许的)。
解决思路
我们设三段分别为 ,,。
由于每一段不能有前导 ,因此:
- 第一段:如果长度大于 ,则第一个字符不能为 ,如果长度为 ,可以是 。
- 第二段:如果长度大于 ,则 不能为 。
- 第三段:如果长度大于 ,则 不能为 。
直接枚举 和 的话,时间复杂度为 ,肯定会炸掉,因此考虑优化。
首先注意到有前缀和后缀,所以考虑预处理前缀模和后缀模,用 表示子串 模 的值,用 表示子串 模 的值,用 表示子串 模 的值。
然后我们发现 有前缀影响,所以我们需要消除前缀影响,并且需要快速判断是否存在 使得满足条件。
需要 ,则:
将等式变形
$$sumq_i \equiv sumq_j \times (10^{j-i})^{-1} \bmod q $$其中 是 在模 下的逆元
为了将问题转化为等值问题,可以引入逆元,设
逆元数组 ,满足:
$$sumq_i \times (10^j)^{-1} \equiv sumq_j \times cir_d \times (10^j)^{-1} $$定义
化简后得:
这样,我们就可以通过统计 的值来快速找到符合条件的 。
关于枚举顺序,考虑枚举第一段结束位置 并动态维护 的位置,用指针 从后向前扫描,将满足合法 的 丢进数组里,最后再分类讨论一下就做完了。
Ac Code
#include<bits/stdc++.h> #define int long long using namespace std; int k,p,q,r,sump[1992929],sumq[1992929],hour[1992929],zhir[1992929]; int hash1[1992929],pw[1992929],cir[1992929],ans1[1992929]; string s; int ksm(int a,int b,int mod){//快速幂 int res=1; while(b){ if(b&1) res=(res*a)%mod; a=(a*a)%mod,b>>=1; } return res; } signed main() { ios::sync_with_stdio(false); cin.tie(0); cin>>k>>p>>q>>r; cin>>s; //预处理前缀模值 for(int i=1;i<=k;i++){ sump[i]=((sump[i-1]*10)%p+(s[i-1]-'0'))%p; sumq[i]=((sumq[i-1]*10)%q+(s[i-1]-'0'))%q; } //预处理后缀模值 int base=1; for(int i=k-1;i>=0;i--){ hour[i]=((s[i]-'0')*base+hour[i+1])%r; base=(base*10)%r; } //验证第三段的合法性 for(int i=0;i<k;i++){//表示从i开始的第三段是否合法 int len=k-i;//第三段长度 if(len==1){ zhir[i]=(hour[i]%r==0); }else{ zhir[i]=((hour[i]%r==0)&&(s[i]!='0')); } } pw[0]=1;//预处理10的幂 for(int i=1;i<=k;i++){ pw[i]=(pw[i-1]*10)%q; } //求逆元 for(int i=0;i<=k;i++){ cir[i]=ksm(pw[i],q-2,q);//// 费马小定理求逆元 } for(int i=0;i<=k;i++){ hash1[i]=(sumq[i]*cir[i])%q; } //枚举划分方案 int ans=0,last=k-1; for(int i=k-2;i>=1;i--){//倒序枚举第一段结束位置i(第一段长度i) if(i>1&&s[0]=='0')continue; if(sump[i]%p!=0)continue; //将满足j>=i+2的合法第三段起始位置j加入ans1 while(last>=i+2){ if(zhir[last]){ int H=hash1[last]; while(H<0)H+=q; H%=q; ans1[H]++; } last--; } //情况1:第二段长度为1(j=i+1) if(i+1<k){ if(zhir[i+1]){ if(s[i]=='0'){ ans++; } } } //情况2:第二段长度>1 if(s[i]!='0'){ int H=hash1[i]; while(H<0)H+=q; H%=q; ans+=ans1[H]; } } cout<<ans; return 0; }2025-07-29 修改了一些公式推导的小问题(这次修改求管理通过)
- 1
信息
- ID
- 7373
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者