http://www.lydsy.com/JudgeOnline/problem.php?id=4542
题意:有一个仅包含'0'~'9'的字符串S[1...N]和一个质数P,M次询问,每次给定正整数L,R,询问有多少个不同位置的允许前导0的子串S[L0...R0]满足L<=L0且R0<=R,且这个子串对应的十进制数时是P的倍数。N,M<=不知道是100,000还是这200,000,P<=10,000,000,000。允许离线。
考虑如何快速判断一个子串S[L0...R0]对应的十进制数是否为P的倍数。
考虑预处理好每个后缀S[i...N]模P的值f(i),其中f(i+1)=0,则模P意义下,S[L0...R0]对应的十进制数*10^(N-R0)=f(L0)-f(R0+1)。
那么,当10^(N-R0)与P互质时,如果要使得S[L0...R0]对应的十进制数在模P意义下等于0,f(L0)必须等于f(R0+1)。
这就将原问题转化为询问区间[L,R+1]内有多少对f(i)相等,这是经典问题,可以用莫队来解决。
注意要特判一下10^(N-R0)与P不互质即P=2或5的情况,此时一个子串对应的十进制数是P的倍数,当且仅当子串的最后一位是P的倍数,这部分的做法非常简单,这里不再赘述。
外话:
这题是HNOI2016Day2T3,因为一些奇怪的原因我成为了场外选手,当时看出了这道唯一的送分题(其他的题目都是大 数据结构题),然后因为并不是很清楚莫队哪一套理论,乱搞了两个多小时才搞出来,敲得我意识模糊……
还有,考场上的数据范围是100,000,bzoj上写的也是100,000,但实际数据范围好像是200,000……因此如果要贴我代码,请把那句define N 100005改成define N 200005。
(现场)代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rpt(i,l,r) for(int i=l;i<=r;i++)
#define rpd(i,r,l) for(int i=r;i>=l;i--)
#define N 100005
long long P;
char s[N];
int n,m,d,nl,nr;
int dv[N];
long long temp;
long long a[N],dict[N];
int b[N],c[N];
struct query{
int l,r,x;
}q[N];
inline bool cmp(query a,query b){
return dv[a.l]==dv[b.l]?a.r<b.r:dv[a.l]<dv[b.l];
}
long long ans_,ans[N];
int main(){
//%%%Claris %%%CreationAugust %%%MedalPluS %%%Hillan %%%YJQQQAQ %%%NanoApe %%%AwD
std::cin>>P;
scanf("%s",s+1);
n=strlen(s+1);
if(P==2||P==5){
rpt(i,1,n) if((s[i]-'0')%P==0) a[i]=i,b[i]=1;
a[0]=0;rpt(i,1,n) a[i]+=a[i-1];
b[0]=0;rpt(i,1,n) b[i]+=b[i-1];
scanf("%d",&m);
while(m--){
scanf("%d%d",&nl,&nr);
printf("%lld\n",a[nr]-a[nl-1]-1LL*(b[nr]-b[nl-1])*(nl-1));
}
}
else{
a[n+1]=0;temp=1;
rpd(i,n,1) a[i]=((s[i]-'0')*temp+a[i+1])%P,(temp*=10)%=P;
n++;rpt(i,1,n) dict[i]=a[i];
std::sort(dict+1,dict+n+1);
rpt(i,1,n) b[i]=std::lower_bound(dict+1,dict+n+1,a[i])-dict;
scanf("%d",&m);
rpt(i,1,m) scanf("%d%d",&q[i].l,&q[i].r),q[i].r++,q[i].x=i;
d=int(sqrt(n));rpt(i,1,n) dv[i]=i/d;
std::sort(q+1,q+m+1,cmp);
memset(c,0,sizeof(c));
nl=1;nr=0;ans_=0;
rpt(i,1,m){
if(nr<q[i].r){
rpt(j,nr+1,q[i].r) ans_+=c[b[j]]++;
nr=q[i].r;
}
if(nl>q[i].l){
rpd(j,nl-1,q[i].l) ans_+=c[b[j]]++;
nl=q[i].l;
}
if(nr>q[i].r){
rpt(j,q[i].r+1,nr) ans_-=--c[b[j]];
nr=q[i].r;
}
if(nl<q[i].l){
rpd(j,q[i].l-1,nl) ans_-=--c[b[j]];
nl=q[i].l;
}
ans[q[i].x]=ans_;
}
rpt(i,1,m) printf("%lld\n",ans[i]);
}
}