[bzoj4277]Ciecie

题目描述

给定一个长度为k的数字串N以及三个质数p,q,r,请你将N划分为三段非空字符串,使得第一段能被p整除,第二段能被q整除,第三段能被r整除,且每一段都不含前导0。
注意:单独的0是允许的。
2015<=p,q,r

瞎做

我们可以很容易通过前缀和后缀和处理判断一个前缀或后缀是不是p或r的倍数。
这个q的倍数呢?
假如[i,j]是q的倍数。
q|jk=ia[k]10jk
q|10jjk=ia[k](10)k
注意q>=2015且是质数,所以10^j可略去。
后面sigma式子只与k有关,因此可以处理那个东西的前缀和。
[i,j]是q的倍数,被转化为
q|sum[j]sum[i1]
sum[i1]sum[j](mod q)
然后就很容易做了,开个桶统计sum模q的合法后缀有多少个,瞎扫一波。
注意不能含有前导0但是一个0是允许的。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--) 
using namespace std;
typedef long long ll;
const int maxn=1000000+10,maxq=100000+10;
int mi[maxn],pre[maxn],suf[maxn],sum[maxn],b[maxq],a[maxn];
int i,j,k,l,t,n,m,p,q,r,mo;
ll ans;
int quicksortmi(int x,int y){
    if (!y) return 1;
    int t=quicksortmi(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
int get(){
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    return ch-'0';
}
bool pd(int i){
    if (i>1&&!a[1]) return 0;
    return (pre[i]==0);
}
bool dp(int i){
    if (i<n&&!a[i]) return 0;
    return (suf[i]==0);
}
int main(){
    scanf("%d%d%d%d",&n,&p,&q,&r);
    fo(i,1,n) a[i]=get();
    mo=p;
    pre[0]=0;
    fo(i,1,n) pre[i]=((ll)pre[i-1]*10%mo+a[i])%mo;
    mo=q;
    t=quicksortmi(10,mo-2);
    l=1;
    fo(i,1,n){
        l=(ll)l*t%mo;
        sum[i]=(sum[i-1]+(ll)a[i]*l%mo)%mo;
    }
    mo=r;
    suf[n+1]=0;
    l=1;
    fd(i,n,1){
        suf[i]=(suf[i+1]+(ll)a[i]*l%mo)%mo;
        l=(ll)l*10%mo;
    }
    fd(i,n,1){
        if (i>1&&i<n){
            if (!a[i]&&pd(i-1)&&dp(i+1)) ans++;
            else if (a[i]&&pd(i-1)) ans+=b[sum[i-1]];
        }
        if (i==n&&!a[i]) b[sum[i-1]]++;
        else if (a[i]&&dp(i)) b[sum[i-1]]++;
    }
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值