[bzoj4542][Hnoi2016]大数——同余+莫队
题目大意:
给定一个质数p和一串数字序列,每次询问一个区间[L,R]中有多少个子区间表示的数为p的倍数。
思路:
首先考虑如何判断一段数字是不是p的倍数,不难想到可以用模p意义下的值来判断,但是这样最多便有可能会有\(n^2\)个余数,每一次计算也需要区间长度的时间,不太方便。
考虑记录以每一个点为起点的后缀所表示的数字在模p下的结果\(sum_i\),对于任意一段区间[L,R],不难发现\(sum_l-sum_{r+1}\)所表示的是[L,R]所表示的数\(\times 10^x\),对于素数里面只有2,5是有可能整除后面的\(10^x\),于是我们只需要对2,5特殊判断一下,其他的素数直接用\(sum_l-sum_{r+1}\)对于p取模的结果来判断就好了。
这样对于任意一个区间[L,R],我们只需要看\(sum_l\)和\(sum_{r+1}\)是否相同,题目便转化为了数一个区间内的相同颜色的个数并求其贡献,这种模型直接用莫队维护即可。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj4542.in","r",stdin);
freopen("bzoj4542.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e5+10;
int n,p,m,a[maxn];
char str[maxn];
namespace subtask1{
ll s1[maxn],s2[maxn];
void work(){
REP(i,1,n){
s1[i]+=s1[i-1];
s2[i]+=s2[i-1];
if(a[i]%p==0){
++s1[i];
s2[i]+=i;
}
}
int l,r;
REP(i,1,m){
read(l),read(r);
printf("%lld\n",s2[r]-s2[l-1]-(s1[r]-s1[l-1])*(l-1));
}
}
}
namespace subtask2{
int tot,bel[maxn];
ll sum[maxn],p10[maxn],b[maxn],ans[maxn],ton[maxn],now;
struct Query{
int l,r,id;
bool operator < (const Query & tt) const {
if(bel[l]==bel[tt.l])return r<tt.r;
return bel[l]<bel[tt.l];
}
}qu[maxn];
void calc(int pos,int ty){
int w=sum[pos];
now-=ton[w]*(ton[w]-1)/2;
ton[w]+=ty;
now+=ton[w]*(ton[w]-1)/2;
}
void work(){
p10[0]=1;
REP(i,1,n)p10[i]=p10[i-1]*10%p;
ll ss=0;
DREP(i,n,1){
ss=(ss+a[i]*p10[n-i])%p;
sum[i]=(ss+p)%p;
}
REP(i,1,n+1)b[++tot]=sum[i];
sort(b+1,b+tot+1);
tot=unique(b+1,b+tot+1)-b-1;
REP(i,1,n+1)sum[i]=lower_bound(b+1,b+tot+1,sum[i])-b;
REP(i,1,n)bel[i]=(i-1)/400+1;
REP(i,1,m)read(qu[i].l),read(qu[i].r),qu[i].id=i;
sort(qu+1,qu+m+1);
int L=1,R=0;
REP(i,1,m){
int l=qu[i].l,r=qu[i].r;
while(L>l)calc(L-1,1),--L;
while(R<r+1)calc(R+1,1),++R;
while(L<l)calc(L,-1),++L;
while(R>r+1)calc(R,-1),--R;
ans[qu[i].id]=now;
}
REP(i,1,m)printf("%lld\n",ans[i]);
}
}
int main(){
File();
read(p);
scanf("%s",str+1);
n=strlen(str+1);
REP(i,1,n)a[i]=str[i]^'0';
read(m);
if(p==2 || p==5)return subtask1::work(),0;
else return subtask2::work(),0;
return 0;
}