bzoj4542 大数

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

你可能感兴趣的:(bzoj4542 大数)