bzoj4542 [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。

HINT

 2016.4.19新加數據一組


正解:莫隊算法。

考慮每次端點移動以後能產生的新貢獻。用一個數組val[i]表示這個數只保留前i位上的數,其他位都是0的數模p的餘數。那麼如果val[l-1]=val[r],那麼[l,r]區間上這個數模p=0。所以只要求出這個數組,並把n+1,同時n+1位爲0,所有詢問的右端點+1,這樣,就能很好處理了。當指針移動時,只要查詢與當前val相等的個數即可。因爲val很大,所以要離散化。注意p=2或5時要加特判,因爲只要最後一位模2或5爲0,那麼這個數就是2或5的倍數。所以就很容易了。


//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<30)
#define il inline
#define RG register
#define ull unsigned long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)

using namespace std;

struct node{ ull l,r,i; }q[100010];

ull c[100010],bl[100010],po[100010],val[100010],num[100010],hsh[100010],ans[100010],pp[100010],pre[100010],n,m,p,tot,block;
char s[100010];

il ull gll(){
    RG ull x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
}

il int cmp(const node &a,const node &b){ return bl[a.l]<bl[b.l] || (bl[a.l]==bl[b.l] && a.r<b.r); }

il void solve(){
    for (RG ull i=1;i<=n;++i) pre[i]=pre[i-1]+((s[i]-48)%p==0),pp[i]=pp[i-1]+i*((s[i]-48)%p==0);
    for (RG ull i=1;i<=m;++i){
	RG ull l=gll(),r=gll();
	printf("%llu\n",pp[r]-pp[l-1]-(pre[r]-pre[l-1])*(l-1));
    }
    return;
}

il void work(){
    scanf("%llu%s%llu",&p,s+1,&m); n=strlen(s+1);
    if (p==2 || p==5){ solve(); return; } block=sqrt(n);
    for (RG ull i=1;i<=m;++i) q[i].l=gll(),q[i].r=gll()+1,q[i].i=i;
    for (RG ull i=1;i<=n;++i) bl[i]=(i-1)/block+1; sort(q+1,q+m+1,cmp);
    po[n]=1; for (RG ull i=n-1;i;--i) po[i]=po[i+1]*10%p;
    for (RG ull i=n;i;--i) num[i]=val[i]=(val[i+1]+(s[i]-48)*po[i])%p; sort(num+1,num+n+2);
    hsh[tot=1]=num[1]; for (RG ull i=2;i<=n+1;++i) if (num[i]>num[i-1]) hsh[++tot]=num[i];
    for (RG ull i=1;i<=n+1;++i) val[i]=lower_bound(hsh+1,hsh+tot+1,val[i])-hsh; ull L=1,R=0,Ans=0;
    for (RG ull i=1;i<=m;++i){
	while (L>q[i].l) L--,Ans+=c[val[L]],c[val[L]]++;
	while (R<q[i].r) R++,Ans+=c[val[R]],c[val[R]]++;
	while (L<q[i].l) c[val[L]]--,Ans-=c[val[L]],L++;
	while (R>q[i].r) c[val[R]]--,Ans-=c[val[R]],R--;
	ans[q[i].i]=Ans;
    }
    for (RG ull i=1;i<=m;++i) printf("%llu\n",ans[i]); return;
}

int main(){
    File("number");
    work();
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章