[BZOJ4542][莫队]HNOI2016:大数

BZOJ4542

我们考虑怎样的一个大数能够整除质数p:
当然是这个大数每一位乘上10的多少次幂
那求一个前缀的和呢?就是每一位乘上10的多少次幂的前缀和
如何判断一个区间模p是否等于0?只需要判断位置 l l l和位置 r r r的前缀和模p的值是否相等
那这就是个莫队板子题了
注意特判p=2和5的情况,具体怎么判详见此文

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e5+5;
int mod;
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
struct Q{
	int l,r,id,bl;
	friend inline bool operator <(const Q &a,const Q &b){if(a.bl==b.bl) return a.r<b.r;return a.bl<b.bl;}
}q[N];
int sqr=320;
int tong[N],n,m;
char s[N];
int a[N],sum[N],b[N],fans[N],num2[N];
ll num[N];
int main(){
	mod=read();scanf("%s",s);n=strlen(s);
	for(int i=1;i<=n;i++) a[i]=s[i-1]-'0';
	if(mod==5){
		for(int i=1;i<=n;i++) num[i]=num[i-1],num2[i]=num2[i-1],num[i]+=i*(a[i]==0 || a[i]==5),num2[i]+=(a[i]==0 || a[i]==5);
		m=read();
		for(int l,r,i=1;i<=m;i++){
			l=read(),r=read();
			cout<<(num[r]-num[l-1])-1ll*(l-1)*(num2[r]-num2[l-1])<<"\n";
		}
		return 0;
	}
	if(mod==2){
		for(int i=1;i<=n;i++) num[i]=num[i-1],num2[i]=num2[i-1],num[i]+=i*((a[i]&1)^1),num2[i]+=((a[i]&1)^1);
		m=read();
		for(int l,r,i=1;i<=m;i++){
			l=read(),r=read();
			cout<<(num[r]-num[l-1])-1ll*(l-1)*(num2[r]-num2[l-1])<<"\n";
		}
		return 0;
	}
	++n;int cnt=0;
	for(int i=n;i;i--){
		a[i]=mul(a[i],ksm(10,n-i));
		inc(a[i],a[i+1]);
		b[++cnt]=a[i];
	}
	sort(b+1,b+cnt+1);
	cnt=unique(b+1,b+cnt+1)-b-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
	int m=read();
	for(int i=1;i<=m;i++){
		q[i].l=read(),q[i].r=read()+1,q[i].id=i;
		q[i].bl=(q[i].l-1)/sqr+1;
	}
	sort(q+1,q+m+1);
	int l=1,r=0,ans=0;
	for(int i=1;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;
		while(l>ql) --l,++tong[a[l]],ans+=tong[a[l]]-1;
		while(r<qr) ++r,++tong[a[r]],ans+=tong[a[r]]-1;
		while(l<ql) ans-=tong[a[l]]-1,--tong[a[l]],++l;
		while(r>qr) ans-=tong[a[r]]-1,--tong[a[r]],--r;
		fans[q[i].id]=ans;
	}
	for(int i=1;i<=m;i++) cout<<fans[i]<<"\n";
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值