1. 程式人生 > >[bzoj4542][莫隊演算法]大數

[bzoj4542][莫隊演算法]大數

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

題解

求一下字尾和 s u m [

i ] sum[i]
不妨設 ( l , r )
(l,r)
組成的數為 n u m num ,顯然 s u m [ l ] s u m [ r + 1 ] = n u m 1 0 r l sum[l]-sum[r+1]=num*10^{r-l}
如果 n u m 0 num\equiv 0 ,在 P 2 , 5 P\ne2,5 下,顯然 1 0 r l 0 10^{r-l}\ne0
於是這時候 s u m [ l ] s u m [ r + 1 ] = 0 sum[l]-sum[r+1]=0 即可
然後就是要求區間裡相同的數數量
離散化一下莫隊就行了…
P = 2 , 5 P =2,5 的情況下,顯然只和最後一位有關…字首和搞搞即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const int MAXN=100010;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
LL stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(LL x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}

int n,m;LL mod;
char ch[MAXN];

LL sum[MAXN];
struct LSnode{LL y,p;}nu[MAXN];
bool cmp1(LSnode n1,LSnode n2){return n1.y<n2.y;}
int rec[MAXN],cal[MAXN];

LL answer[MAXN],ans;
int pos[MAXN],block;
struct ask{int l,r,op;}w[MAXN];
bool cmp(ask n1,ask n2){return pos[n1.l]==pos[n2.l]?n1.r<n2.r:pos[n1.l]<pos[n2.l];}

LL pre[MAXN];
void up1(int now)
{
	ans+=cal[rec[now]];
	cal[rec[now]]++;
}
void up2(int now)
{
	cal[rec[now]]--;
	ans-=cal[rec[now]];
}
int s1[MAXN],s2[MAXN];
LL g1[MAXN],g2[MAXN];
int main()
{
	mod=read();
	scanf("%s",ch+1);
	n=strlen(ch+1);block=sqrt(n+1);
	pre[0]=1;for(int i=1;i<=MAXN-10;i++)pre[i]=pre[i-1]*10%mod;
	for(int i=1;i<=n;i++)pos[i]=(i-1)/block+1;
	m=read();
	for(int i=1;i<=m;i++)w[i].l=read(),w[i].r=read()+1,w[i].op=i;
	for(int i=n;i>=1;i--)sum[i]=((ch[i]-'0')*pre[n-i]%mod+sum[i+1])%mod,nu[i].y=sum[i],nu[i].p=i;

	sort(nu+1,nu+1+n,cmp1);
	int tt=0;
	for(int i=1;i<=n;i++)
	{
		if(nu[i].y!=nu[i-1].y)tt++;
		rec[nu[i].p]=tt;
	}
	
	for(int i=1;i<=n;i++)
	{
		int num=ch[i]-'0';
		s1[i]=s1[i-1]+(num%2==0);s2[i]=s2[i-1]+(num%5==0);
		g1[i]=g1[i-1]+(num%2==0?i:0);
		g2[i]=g2[i-1]+(num%5==0?i:0);
	}
	if(mod==2||mod==5)
	{
		for(int i=1;i<=m;i++)
		{
			LL aa;w[i].r--;
			if(mod==5)aa=g2[w[i].r]-g2[w[i].l-1]-(LL)(w[i].l-1)*(s2[w[i].r]-s2[w[i].l-1]);
			else aa=g1[w[i].r]-g1[w[i].l-1]-(LL)(w[i].l-1)*(s1[w[i].r]-s1[w[i].l-1]);
			pr2(aa);
		}
		return 0;
	}
	
	sort(w+1,w+1+m,cmp);
	
	for(int i=w[1].l;i<=w[1].r;i++)
	{
		ans+=cal[rec[i]];
		cal[rec[i]]++;
	}
	int l=w[1].l,r=w[1].r;answer[w[1].op]=ans;
	
	for(int i=2;i<=m;i++)
	{
		while(l>w[i].l)up1(--l);
		while(l<w[i].l)up2(l++);
		while(r>w[i].r)up2(r--);
		while(r<w[i].r)up1(++r);
		answer[w[i].op]=ans;
	}
	for(int i=1;i<=m;i++)pr2(answer[i]);
	return 0;
}