BZOJ 4542: [Hnoi2016]大数
4542: [Hnoi2016]大数
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 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
121121
3
1 6
1 5
1 4
Sample Output
5
3
2
//第一个询问问的是整个串,满足条件的子串分别有:121121,2112,11,121,121。
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[n]=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