【BZOJ4542】[Hnoi2016]大数 莫队
【BZOJ4542】[Hnoi2016]大数
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]为 213。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。
题解:看到题容易想到用莫队。用sum[i]表示S的前i位组成的数%P的值,那么如果i...j能组成一个%P=0的数,意味着
$sum[j]-sum[i]*10^{j-i}=0(mod P) \rightarrow sum[j]*10^{-j}=sum[i]*10^{-i} (mod P)$
所以离散化一下,然后就变成了问一个区间中有多少对相同数,的用桶+莫队即可。
注意特判P=2,P=5的情况!因为上式不再成立!具体方法是直接判断哪些串的末尾的数能被2和5整除即可。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int maxn=100010; int B,P,n,m,nm; char str[maxn]; int v[maxn],s[maxn]; ll sum,ans[maxn]; ll s1[maxn],s2[maxn]; struct node { int val,org; }p[maxn]; struct query { int l,r,org; }q[maxn]; bool cmpp(node a,node b) { return a.val<b.val; } bool cmpq(query a,query b) { return (a.l/B==b.l/B)?(a.r<b.r):(a.l/B<b.l/B); } int main() { scanf("%d%s%d",&P,str,&m),n=strlen(str); int i,l,r; if(P==2||P==5) { for(i=1;i<=n;i++) { s1[i]=s1[i-1],s2[i]=s2[i-1]; if((str[i-1]-'0')%P==0) s1[i]+=i,s2[i]++; } for(i=1;i<=m;i++) { scanf("%d%d",&l,&r); printf("%lld\n",s1[r]-s1[l-1]-(l-1)*(s2[r]-s2[l-1])); } return 0; } p[0].val=0; for(i=1;i<=n;i++) p[i].val=(10ll*p[i-1].val+str[i-1]-'0')%P,p[i].org=i; ll tmp=1; for(i=n;i>=1;i--,tmp=tmp*10%P) p[i].val=tmp*p[i].val%P; sort(p,p+n+1,cmpp); for(i=0;i<=n;i++) { if(!i||p[i].val>p[i-1].val) nm++; v[p[i].org]=nm; } B=int(sqrt(double(n))); for(i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].l--,q[i].org=i; sort(q+1,q+m+1,cmpq); l=1,r=0; for(i=1;i<=m;i++) { while(l>q[i].l) l--,sum+=s[v[l]],s[v[l]]++; while(l<q[i].l) s[v[l]]--,sum-=s[v[l]],l++; while(r<q[i].r) r++,sum+=s[v[r]],s[v[r]]++; while(r>q[i].r) s[v[r]]--,sum-=s[v[r]],r--; ans[q[i].org]=sum; } for(i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
| 欢迎来原网站坐坐! >原文链接<