1. 程式人生 > >[bzoj4542][Hnoi2016]大數——同餘+莫隊

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