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; }