BZOJ 4542: [Hnoi2016]大数

4542: [Hnoi2016]大数

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 1354  Solved: 470
[Submit][Status][Discuss]

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新加数据一组

Source

分析:

考虑如果一个子串如果能够被$p$整除,就代表$s[l,r]%p==0$,这是区间的形式,那么考虑转化成后缀的形式:$s[l]%p-s[r+1]%p==0$,也就是说$s[l]%p==s[r+1]%p$,诶,忽然发现这是经典的莫队问题...询问区间内相等的数对个数...

但是这个方法不适用于$2$和$5$,因为就算是$s[l]%p!=s[r+1]$子串也有可能合法,并且这两个数只需要判断最后一位就好,所以前缀和维护...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
//by NeighThorn
using namespace std;

const int maxn=100000+5;

int n,m,p,blo,len,id[maxn],cnt[maxn];
long long tmp,no[maxn],mp[maxn],ans[maxn],sum[maxn],power[maxn];

char s[maxn];

struct M{
	int l,r,num;
	friend bool operator < (M a,M b){
		if(id[a.l]!=id[b.l])
			return id[a.l]<id[b.l];
		return a.r<b.r;
	}
}q[maxn];

inline int read(void){
	char ch=getchar();int x=0;
	while(!(ch>='0'&&ch<='9')) ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}

inline void change(int x,int val){
	x=no[x];
	if(cnt[x]>1) tmp-=1LL*cnt[x]*(cnt[x]-1)/2;
	cnt[x]+=val;
	if(cnt[x]>1) tmp+=1LL*cnt[x]*(cnt[x]-1)/2;
}

inline int check(char a){
	int x=a-'0';
	if(p==2){
		if(x&1) return 0;
		return 1;
	}
	else if(p==5){
		if(x==0||x==5) return 1;
		return 0;
	}
}

inline void solve1(void){
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+check(s[i])*i,no[i]=no[i-1]+check(s[i]);
	for(int i=1,l,r;i<=m;i++)
		l=read(),r=read(),printf("%lld\n",sum[r]-sum[l-1]-1LL*(no[r]-no[l-1])*(l-1));
}

signed main(void){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	p=read(),scanf("%s",s+1),m=read(),n=strlen(s+1);blo=sqrt(n);
	if(p==2||p==5) return solve1(),0;
	for(int i=1;i<=n;i++) id[i]=(i-1)/blo+1;power[n]=1;
	for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read()+1,q[i].num=i;
	for(int i=n-1;i>=1;i--) power[i]=1LL*power[i+1]*10%p;
	for(int i=n;i>=1;i--)
		no[i]=(no[i+1]+1LL*power[i]*(s[i]-'0')%p)%p,mp[i]=no[i];mp[n+1]=0;
	sort(mp+1,mp+1+n+1);len=unique(mp+1,mp+n+1+1)-mp-1;sort(q+1,q+m+1);
	for(int i=1;i<=n+1;i++) no[i]=lower_bound(mp+1,mp+len+1,no[i])-mp;
	for(int i=1,l=1,r=0;i<=m;i++){
		for(;l<q[i].l;l++) change(l  ,-1);
		for(;l>q[i].l;l--) change(l-1, 1);
		for(;r<q[i].r;r++) change(r+1, 1);
		for(;r>q[i].r;r--) change(r  ,-1);
		ans[q[i].num]=tmp;
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

 


By NeighThorn

posted @ 2017-03-24 21:28  NeighThorn  阅读(257)  评论(0编辑  收藏  举报