1. 程式人生 > >【HNOI2016】大數

【HNOI2016】大數

dot 位置 href 等於 getch 問題 for ++ esp

【HNOI2016】大數

題目鏈接

題目描述

小 B 有一個很大的數 $ S $,長度達到了 $ N $ 位;這個數可以看成是一個串,它可能有前導 $ 0 $,例如 00009312345 。小 B 還有一個素數 $ P $。

現在,小 B 提出了 $ M $ 個詢問,每個詢問求 $ S $ 的一個子串中有多少子串是 $ P $ 的倍數($ 0 $ 也是 $ P $ 的倍數)。例如 $ S $ 為 0077 時,其子串 007 有六個子串:0, 0, 7, 00, 07, 007;顯然 0077 的子串 077 的六個子串都是素數 $ 7 $ 的倍數。

輸入格式

第一行一個整數:$ P $。

第二行一個串:$ S $。

第三行一個整數:$ M $。

接下來 $ M $ 行,每行兩個整數 $ \text{fr}, \text{to}$,表示對 $ S $ 的子串 \(S[\text{fr} \ldots \text {to}]\) 的一次詢問。

註意:$ S $ 的最左端的數字的位置序號為 $ 1 $;例如 $ S $ 為 $ 213567 $,則 $ S[1] $ 為 $ 2 \(,\) S[1 \ldots 3] $為 $ 213 $。

輸出格式

輸出 $ M $ 行,每行一個整數,第 $ i $ 行是第 $ i $ 個詢問的答案。

樣例

樣例輸入

11 
121121 
3 
1 6 
1 5 
1 4

樣例輸出

5
3
2

樣例解釋

第一個詢問問的是整個串,滿足條件的子串分別有:121121211211121121

數據範圍與提示

對於所有的數據,$ N,M \leq 100000 \(,\) P $ 為素數。

參考題解

我們要求的是
\[ \displaystyle \begin{align} ans&=\sum_{i=l}^r\sum_{j=i}^r [\sum_{k=i}^js_k\cdot 10^{j-k} ==0\ mod\ p]\&=\sum_{i=l}^r\sum_{j=i}^r10^{j}[\sum_{k=i}^js_k\cdot 10^{-k} ==0\ mod\ p] \end{align} \]


如果質數\(P\)不等於\(2\)\(5\),那麽\(10^j\)不可能等於\(0\),並且\(10^k\)是有逆元的。

我們設\(s_k\cdot 10^{-k}\)的前綴和為\(sum_k\),則我們要求的就是:
\[ \displaystyle \sum_{i=l}^r\sum_{j=i}^r[sum_j==sum_i] \]
這就是一個經典的莫隊問題。

類似的題還有【CQOI2018】 異或序列。

\(P\)等於\(2\)或者\(5\)的時候,我們知道只需要判斷一個字串的最後一位就可以了,所以很好做。

代碼:

#include<bits/stdc++.h>
#define ll long long
#define N 200005

using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

ll p;
char s[N];
int n,m;
int bel[N];
ll ans[N];
ll sum[N],d[N];
ll cnt[N];
const int blk=450;
struct query {
    int l,r;
    int id;
    bool operator <(const query &a)const {
        if(bel[l]!=bel[a.l]) return l<a.l;
        return bel[l]&1?r<a.r:r>a.r;
    }
}q[N];

ll ksm(ll t,ll x,ll mod) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

ll now=0;
void add(int v) {
    now+=cnt[sum[v]];
    cnt[sum[v]]++;
}

void del(int v) {
    cnt[sum[v]]--;
    now-=cnt[sum[v]];
}

ll tot[N],size[N];
int main() {
    p=Get();
    scanf("%s",s+1);
    n=strlen(s+1);
    m=Get();
    for(int i=1;i<=m;i++) {
        q[i].l=Get()-1,q[i].r=Get();
        q[i].id=i;
    }
    
    if(p!=2&&p!=5) {
        for(int i=0;i<=n;i++) bel[i]=i/blk+1;
        sort(q+1,q+1+m);
        d[++d[0]]=0;
        ll inv10=ksm(10,p-2,p),t=inv10;
        for(int i=1;i<=n;i++) {
            sum[i]=(sum[i-1]+(s[i]-'0')*t)%p;
            t=t*inv10%p;
            d[++d[0]]=sum[i];
        }
        sort(d+1,d+1+d[0]);
        int cc=unique(d+1,d+1+d[0])-d-1;
        for(int i=0;i<=n;i++) sum[i]=lower_bound(d+1,d+1+cc,sum[i])-d;
        
        int l=0,r=-1;
        for(int i=1;i<=m;i++) {
            while(r<q[i].r) add(++r);
            while(l>q[i].l) add(--l);
            while(r>q[i].r) del(r--);
            while(l<q[i].l) del(l++);
            ans[q[i].id]=now;
        }
        for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    } else {
        if(p==2) {
            for(int i=1;i<=n;i++) {
                tot[i]=tot[i-1];
                size[i]=size[i-1];
                if((s[i]-'0')%2==0) {
                    tot[i]+=i;
                    size[i]++;
                }
            }
        } else {
            for(int i=1;i<=n;i++) {
                tot[i]=tot[i-1];
                size[i]=size[i-1];
                if((s[i]-'0')%5==0) {
                    tot[i]+=i;
                    size[i]++;
                }
            }
        }
        for(int i=1;i<=m;i++) {
            cout<<(tot[q[i].r]-tot[q[i].l])-(size[q[i].r]-size[q[i].l])*q[i].l<<"\n";
        }
    }
    return 0;
}

【HNOI2016】大數