1. 程式人生 > >bzoj 4542: [Hnoi2016]大數 (莫隊)

bzoj 4542: [Hnoi2016]大數 (莫隊)

Description

  小 B 有一個很大的數 S,長度達到了 N 位;這個數可以看成是一個串,它可能有前導 0,例如00009312345
。小B還有一個素數P。現在,小 B 提出了 M 個詢問,每個詢問求 S 的一個子串中有多少子串是 P 的倍數(0 也
是P 的倍數)。例如 S為0077時,其子串 007有6個子串:0,0,7,00,07,007;顯然0077的子串007有6個子串都是素
數7的倍數。

Input

  第一行一個整數:P。第二行一個串:S。第三行一個整數:M。接下來M行,每行兩個整數 fr,to,表示對S 的
子串S[fr…to]的一次詢問。注意:S的最左端的數字的位置序號為 1;例如S為213567,則S[1]為 2,S[1…3]為 2
13。N,M<=100000,P為素數

Output

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

Sample Input

11
121121
3
1 6
1 5
1 4

Sample Output

5
3
2
//第一個詢問問的是整個串,滿足條件的子串分別有:121121,2112,11,121,121。
  實現程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll M = 1e5 + 10;
ll blo;
struct node{ ll id,l,r; bool operator < (const node &k) const { if(l/blo == k.l/blo) return r < k.r; return l/blo < k.l/blo; } }q[M]; ll a[M],hs[M]; ll ba[M],ans[M]; char s[M]; map<ll,ll>mp; int main() { ll p,m,l=1,r=0; scanf("%lld%s%lld
",&p,s+1,&m); ll n = strlen(s+1); blo = (ll)sqrt(n*1.0); if(p !=2 &&p != 5){ ll num = 1; for(ll i = n;i >= 1;i --){ a[i] = (a[i+1]+(s[i]-48)*num)%p; num = num*10%p; hs[i] = a[i]; } sort(hs+1,hs+2+n); ll siz = unique(hs+1,hs+2+n)-hs-1; for(int i = 1;i <= n+1;i ++) a[i] = lower_bound(hs+1,hs+2+siz,a[i])-hs; for(ll i = 1;i <= m;i ++){ scanf("%lld%lld",&q[i].l,&q[i].r); q[i].id = i; q[i].r ++; } sort(q+1,q+m+1); ll cnt = 0; for(ll i = 1;i <= m;i ++){ while(r < q[i].r) cnt += ba[a[++r]]++; while(l > q[i].l) cnt += ba[a[--l]]++; while(l < q[i].l) cnt -= --ba[a[l++]]; while(r > q[i].r) cnt -= --ba[a[r--]]; ans[q[i].id] = cnt; } for(ll i = 1;i <= m;i ++) printf("%lld\n",ans[i]); } else{ for(ll i = 1;i <= n;i ++){ if(!((s[i]-48)%p)) ba[i] = ba[i-1]+1,hs[i] = hs[i-1]+i; else ba[i] = ba[i-1],hs[i] = hs[i-1]; } for(ll i = 1;i <= m;i ++){ scanf("%lld%lld",&l,&r); printf("%lld\n",hs[r]-hs[l-1]-(ba[r]-ba[l-1])*(l-1)); } } }