Bzoj4542--Hnoi2016大数

一开始想到过用莫队,没有想出来怎么去转移。。。

事实上对于一个子串是p的倍数的串有一个性质,就是开头和结尾的后缀串的余数相同,这个还是好理解的。

假设一个子串[l,r]数值为t,设t=xp,这个子串结尾对应的原串后缀[r+1,n]数值为t',设t'=y*p+r

那么这个串开头对应原串的后缀[l,n]值为w=t*10^(n-r+1)+t'=(x*10^(n-r+1)+y)*p+r,显然w%p=r

但是对于10的两个质因子2,5是不成立的,因为t*10^(n-r+1)%P=0

那么我们对于2和5单独来处理,2,5的话就只要结尾是2,5的倍数的都可以就可以简单求解了

代码: 

#include<bits/stdc++.h>
#define LL long long
using namespace std;

inline int read() {
    int ret=0,f=1;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
    return ret*f;
}

#define MAXN 100005
#define MAXM 100005

int P,n,m,bl[MAXN],qsz,hs[MAXN];
char s[MAXN];LL te[MAXN],re[MAXN];
struct que{
    int l,r,id;
}q[MAXM];LL ans[MAXM];

inline bool cmp(const que &a,const que &b) {
    return bl[a.l]==bl[b.l]?a.r<b.r:a.l<b.l;
}

int BS(int v) {
    int l=0,r=n,mid,ret;
    while(l<=r) {
        mid=l+r>>1;
        if(hs[mid]>=v) r=mid-1,ret=mid;
        else l=mid+1;
    }
    return ret;
}

int nl=1,nr=0,ap[MAXN];LL now;
inline void Chg(int v,int p) {
    ap[v]+=p;now+=p>0?ap[v]-1:-ap[v];
}

int main() {
    P=read();
    scanf("%s",s+1);te[0]=1;
    n=strlen(s+1);qsz=sqrt(n);
    for(int i=1;i<=n;i++) bl[i]=i/qsz,te[i]=te[i-1]*10%P;
    m=read();
    for(int i=1;i<=m;i++) {
        q[i].l=read();q[i].r=read();
        q[i].id=i;
    }
    if(10%P) {
        sort(q+1,q+m+1,cmp);
        for(int i=n;i>0;i--) re[i]=(re[i+1]+(s[i]-'0')*te[n-i])%P,hs[i]=re[i];
        sort(hs+1,hs+1+n);
        for(int i=n;i>0;i--) re[i]=BS(re[i]);
        for(int i=1;i<=m;i++) {
            q[i].r++;
            while(q[i].r>nr) Chg(re[++nr],1);
            while(q[i].l<nl) Chg(re[--nl],1);
            while(q[i].l>nl) Chg(re[nl++],-1);
            while(q[i].r<nr) Chg(re[nr--],-1);
            ans[q[i].id]=now;
        }
    }
    else {
        for(int i=n;i>0;i--) if((s[i]-'0')%P==0) re[i]=i,ap[i]=1;
        for(int i=1;i<=n;i++) re[i]+=re[i-1],ap[i]+=ap[i-1];
        for(int i=1;i<=m;i++) 
            ans[i]=re[q[i].r]-re[q[i].l-1]-(LL)(ap[q[i].r]-ap[q[i].l-1])*(q[i].l-1);
    }
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}

 

posted @ 2016-10-25 10:01  ihopenot  阅读(315)  评论(0编辑  收藏  举报