您的位置:首页 > 其它

BZOJ 4542: [Hnoi2016]大数

2017-03-24 21:28 351 查看

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
=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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: