[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] sum[i]
不妨设 ( l , r ) (l,r) (l,r)组成的数为 n u m num 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} sum[l]sum[r+1]=num10rl
如果 n u m ≡ 0 num\equiv 0 num0,在 P ≠ 2 , 5 P\ne2,5 P̸=2,5下,显然 1 0 r − l ≠ 0 10^{r-l}\ne0 10rl̸=0
于是这时候 s u m [ l ] − s u m [ r + 1 ] = 0 sum[l]-sum[r+1]=0 sum[l]sum[r+1]=0即可
然后就是要求区间里相同的数数量
离散化一下莫队就行了…
P = 2 , 5 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值