[BZOJ 4542][Hnoi2016]大数:莫队

来源:互联网 发布:私家车包车软件 编辑:程序博客网 时间:2024/05/23 21:20

点击这里查看原题

首先可以考虑,除了2和5之外,10k不是任何素数的倍数,因此可以先解决p不为2或5的情况。对从每个位置开始的到末尾结束的子串求一下模p的值,再离散化一下(因为题目没有给出p的范围),这样的话如果两个位置i,j模p的值相同,那么S[i…j-1]就是符合条件的一个子串。莫队处理即可
再来考虑下2和5的情况,可以发现,如果某个数是2或5的倍数,那么它的个位一定也是2或5的倍数,于是前缀和处理即可。

#include<bits/stdc++.h>#define ll long longusing namespace std;const int M=1e5+5;int q,pos[M],n,cnt[M],tot;bool vis[M];char s[M];ll res[M],ans[M],now,v[M],t[M],p;struct no{    int l,r,id;    bool operator<(const no &b)const{        return pos[l]==pos[b.l]?r<b.r:pos[l]<pos[b.l];    }}qu[M];void solve1(){    for(int i=1;i<=n;i++){        res[i]=res[i-1];        cnt[i]=cnt[i-1];        if((s[i]-'0')%p==0){            res[i]+=i;            cnt[i]++;        }    }    while(q--){        int l,r;        scanf("%d%d",&l,&r);        ll tmp=res[r]-res[l-1]-(ll)(cnt[r]-cnt[l-1])*(l-1);        printf("%lld\n",tmp);    }}void update(int x){    now-=(ll)(cnt[v[x]]-1)*cnt[v[x]]/2;    if(!vis[x]) cnt[v[x]]++;    else cnt[v[x]]--;    vis[x]^=1;    now+=(ll)(cnt[v[x]]-1)*cnt[v[x]]/2;}int main(){    scanf("%lld%s%d",&p,s+1,&q);    n=strlen(s+1);    if(p==2||p==5){        solve1();        return 0;    }    for(int i=1,block=sqrt(n)+0.5;i<=n;i++) pos[i]=(i-1)/block+1;    ll x=0,fac=1;    for(int i=n,x=0,fac=1;i;i--){        x=(x+(ll)(s[i]-'0')*fac)%p;        fac=(ll)fac*10%p;        v[i]=t[i]=x;    }    v[++n]=t[n]=0;    sort(t+1,t+n+1);    tot=unique(t+1,t+n+1)-t-1;    for(int i=1;i<=n;i++) v[i]=lower_bound(t+1,t+tot+1,v[i])-t;    for(int i=1;i<=q;i++){        scanf("%d%d",&qu[i].l,&qu[i].r);        qu[i].r++;        qu[i].id=i;    }    sort(qu+1,qu+q+1);    int l=1,r=0;    for(int i=1;i<=q;i++){        for(int j=l;j<qu[i].l;j++) update(j);        for(int j=r;j>qu[i].r;j--) update(j);        for(int j=l-1;j>=qu[i].l;j--) update(j);        for(int j=r+1;j<=qu[i].r;j++) update(j);        l=qu[i].l,r=qu[i].r;        ans[qu[i].id]=now;    }    for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);    return 0;}