[bzoj4542][Hnoi2016]大数——同余+莫队

题目大意:

给定一个质数p和一串数字序列,每次询问一个区间[L,R]中有多少个子区间表示的数为p的倍数。

思路:

首先考虑如何判断一段数字是不是p的倍数,不难想到可以用模p意义下的值来判断,但是这样最多便有可能会有\(n^2\)个余数,每一次计算也需要区间长度的时间,不太方便。
考虑记录以每一个点为起点的后缀所表示的数字在模p下的结果\(sum_i\),对于任意一段区间[L,R],不难发现\(sum_l-sum_{r+1}\)所表示的是[L,R]所表示的数\(\times 10^x\),对于素数里面只有2,5是有可能整除后面的\(10^x\),于是我们只需要对2,5特殊判断一下,其他的素数直接用\(sum_l-sum_{r+1}\)对于p取模的结果来判断就好了。
这样对于任意一个区间[L,R],我们只需要看\(sum_l\)\(sum_{r+1}\)是否相同,题目便转化为了数一个区间内的相同颜色的个数并求其贡献,这种模型直接用莫队维护即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj4542.in","r",stdin);
    freopen("bzoj4542.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=1e5+10;
int n,p,m,a[maxn];
char str[maxn];

namespace subtask1{
    ll s1[maxn],s2[maxn];
    void work(){
        REP(i,1,n){
            s1[i]+=s1[i-1];
            s2[i]+=s2[i-1];
            if(a[i]%p==0){
                ++s1[i];
                s2[i]+=i;
            }
        }
        int l,r;
        REP(i,1,m){
            read(l),read(r);
            printf("%lld\n",s2[r]-s2[l-1]-(s1[r]-s1[l-1])*(l-1));
        }
    }
}

namespace subtask2{
    int tot,bel[maxn];
    ll sum[maxn],p10[maxn],b[maxn],ans[maxn],ton[maxn],now;
    struct Query{
        int l,r,id;
        bool operator < (const Query & tt) const {
            if(bel[l]==bel[tt.l])return r<tt.r;
            return bel[l]<bel[tt.l];
        }
    }qu[maxn];
    void calc(int pos,int ty){
        int w=sum[pos];
        now-=ton[w]*(ton[w]-1)/2;
        ton[w]+=ty;
        now+=ton[w]*(ton[w]-1)/2;
    }
    void work(){
        p10[0]=1;
        REP(i,1,n)p10[i]=p10[i-1]*10%p;
        ll ss=0;
        DREP(i,n,1){
            ss=(ss+a[i]*p10[n-i])%p;
            sum[i]=(ss+p)%p;
        }

        REP(i,1,n+1)b[++tot]=sum[i];
        sort(b+1,b+tot+1);
        tot=unique(b+1,b+tot+1)-b-1;
        REP(i,1,n+1)sum[i]=lower_bound(b+1,b+tot+1,sum[i])-b;

        REP(i,1,n)bel[i]=(i-1)/400+1;
        REP(i,1,m)read(qu[i].l),read(qu[i].r),qu[i].id=i;
        sort(qu+1,qu+m+1);

        int L=1,R=0;
        REP(i,1,m){
            int l=qu[i].l,r=qu[i].r;
            while(L>l)calc(L-1,1),--L;
            while(R<r+1)calc(R+1,1),++R;
            while(L<l)calc(L,-1),++L;
            while(R>r+1)calc(R,-1),--R;
            ans[qu[i].id]=now;
        }
        REP(i,1,m)printf("%lld\n",ans[i]);
    }
}

int main(){
    File();
    read(p);
    scanf("%s",str+1);
    n=strlen(str+1);
    REP(i,1,n)a[i]=str[i]^'0';
    read(m);
    if(p==2 || p==5)return subtask1::work(),0;
    else return subtask2::work(),0;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ylsoi/p/10073511.html