bzoj4542【HNOI2016】大数

4542: [Hnoi2016]大数

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 801   Solved: 282
[ 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新加数据一组




莫队算法

首先有一个性质:如果A*10^k+B=B(mod p),且p和10^k互质,则A%p=0。

那么,要判断一个s[l...r]是否是p的倍数,只要判断s[l...n]和s[r+1...n]在模n的意义下是否相等。

于是,我们可以预处理出每一个后缀s[i...n]对p取模的结果a[i]。然后对于一个询问[l,r],就转化成了询问[l,r+1]中有多少对相同的a[i]。用莫队算法就可以解决了。

当然p如果和10^k不互质,即p=2或p=5的时候要特殊考虑。

(具体实现方法见代码)




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 100010
using namespace std;
int n,m,block,l,r;
ll p,cnt[maxn],sum[maxn],ans[maxn],f[maxn],g[maxn];
char s[maxn];
struct data{int l,r,id,num;}q[maxn];
map<ll,ll> mp;
inline int read()
{
	int x=0,f=1;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;
}
inline bool cmp(data a,data b)
{
	return a.num==b.num?a.r<b.r:a.num<b.num;
}
inline void solve()
{
	F(i,1,n)
	{
		cnt[i]=cnt[i-1];sum[i]=sum[i-1];
		if ((s[i]-'0')%p==0) cnt[i]++,sum[i]+=i;
	}
	m=read();
	F(i,1,m)
	{
		int l=read(),r=read();
		printf("%lld\n",sum[r]-sum[l-1]-(cnt[r]-cnt[l-1])*(l-1));
	}
}
int main()
{
	scanf("%lld%s",&p,s+1);
	n=strlen(s+1);
	if (p==2||p==5)
	{
		solve();
		return 0;
	}
	block=(int)sqrt(n);
	m=read();
	F(i,1,m)
	{
		q[i].l=read();q[i].r=read()+1;
		q[i].id=i;q[i].num=(q[i].l-1)/block+1;
	}
	sort(q+1,q+m+1,cmp);
	ll tmp=1;
	D(i,n,1)
	{
		f[i]=(f[i+1]+tmp*(s[i]-'0')%p)%p;
		tmp=tmp*10%p;
	}
	n++;
	F(i,1,n) g[i]=f[i];
	sort(g+1,g+n+1);
	tmp=0;
	F(i,1,n) if (i==1||g[i]!=g[i-1]) mp[g[i]]=++tmp;
	F(i,1,n) f[i]=mp[f[i]];
	F(i,1,m)
	{
		if (q[i].num!=q[i-1].num)
		{
			l=1;r=0;tmp=0;
			memset(cnt,0,sizeof(cnt));
		}
		while (r<q[i].r){r++;tmp+=cnt[f[r]];cnt[f[r]]++;}
		while (l<q[i].l){cnt[f[l]]--;tmp-=cnt[f[l]];l++;}
		while (l>q[i].l){l--;tmp+=cnt[f[l]];cnt[f[l]]++;}
		ans[q[i].id]=tmp;
	}
	F(i,1,m) printf("%lld\n",ans[i]);
	return 0;
}


你可能感兴趣的:(bzoj,莫队算法)