【莫队】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]为 2
13。N,M<=100000,P为素数

Output

  输出M行,每行一个整数,第 i行是第 i个询问的答案。


题目分析

一个区间[l,r]产生贡献即$number_{i,j}\equiv 0\ ({\rm mod}\ p)$.

按照常见套路来说,应该把区间拆成关于端点的式子。用$pre[i]$表示前$i$位在十进制下的数值,那么即$number_{i,j}=pre[j]-10^{j-i+1}*pre[i-1]$。将式子移项发现左右两边形如$f(r)=f(l-1)$(据说这是一种经典的莫队套路)那么于此已经将区间问题转化为关于单点的可$O(1)$修改的子问题了。

之后的操作就相对套路:将$n+1$个$f(i)$(不要忘记统计$f(0)$)预先离散化处理;然后常规莫队操作。

注意莫队要先修改再更新!

 

  1 #include<cmath>
  2 #include<cstdio>
  3 #include<cctype>
  4 #include<cstring>
  5 #include<algorithm>
  6 const int maxn = 100035;
  7 
  8 int blk[maxn];
  9 struct QRs
 10 {
 11     int l,r,id;
 12     bool operator < (QRs a) const
 13     {
 14         if (blk[l]==blk[a.l]) return r < a.r;
 15         return blk[l] < blk[a.l];
 16     }
 17 }q[maxn];
 18 char s[maxn];
 19 int p,n,m,cnt,tot,size,inv10;
 20 int lbd,rbd,lSd[maxn],rSd[maxn];
 21 int ans[maxn],sum[maxn],pre[maxn],t[maxn];
 22 
 23 int read()
 24 {
 25     char ch = getchar();
 26     int num = 0;
 27     bool fl = 0;
 28     for (; !isdigit(ch); ch=getchar())
 29         if (ch=='-') fl = 1;
 30     for (; isdigit(ch); ch=getchar())
 31         num = (num<<1)+(num<<3)+ch-48;
 32     if (fl) num = -num;
 33     return num;
 34 }
 35 int qmi(int a, int b)
 36 {
 37     int ret = 1;
 38     while (b)
 39     {
 40         if (b&1) ret = 1ll*ret*a%p;
 41         b >>= 1, a = 1ll*a*a%p;
 42     }
 43     return ret;
 44 }
 45 void lAdd(int lbd)
 46 {
 47     rSd[sum[lbd]]++, tot+=rSd[sum[lbd-1]], lSd[sum[lbd-1]]++;
 48 }
 49 void rAdd(int rbd)
 50 {
 51     lSd[sum[rbd-1]]++, tot+=lSd[sum[rbd]], rSd[sum[rbd]]++;
 52 }
 53 void lErase(int lbd)
 54 {
 55     lSd[sum[lbd-1]]--, tot-=rSd[sum[lbd-1]], rSd[sum[lbd]]--;
 56 }
 57 void rErase(int rbd)
 58 {
 59     rSd[sum[rbd]]--, tot-=lSd[sum[rbd]], lSd[sum[rbd-1]]--;
 60 }
 61 int main()
 62 {
 63     freopen("bignum.in","r",stdin);
 64     freopen("bignum.out","w",stdout);
 65     p = read(), scanf("%s",s+1);
 66     n = strlen(s+1), size = sqrt(n+0.5)+5;
 67     if (p==2||p==5){
 68         for (int i=1; i<=n; i++)
 69         {
 70             ans[i] = ans[i-1], sum[i] = sum[i-1];
 71             if ((s[i]-'0')%p==0) ans[i]++, sum[i] += i;
 72         }
 73         m = read();
 74         for (int i=1; i<=m; i++)
 75         {
 76             int l = read(), r = read();
 77             printf("%d\n",sum[r]-sum[l-1]-(ans[r]-ans[l-1])*(l-1));
 78         }
 79         return 0;
 80     }
 81     for (int i=1; i<=n; i++) blk[i] = i/size, pre[i] = (pre[i-1]*10ll%p+s[i]-'0')%p;
 82     inv10 = qmi(10, p-2);
 83     for (int i=0, pr=1; i<=n; i++)
 84         t[i] = sum[i] = 1ll*pre[i]*pr%p, pr = 1ll*pr*inv10%p;
 85     std::sort(t, t+n+1), cnt = std::unique(t, t+n+1)-t-1;
 86     for (int i=0; i<=n; i++)
 87         sum[i] = std::lower_bound(t, t+cnt+1, sum[i])-t+1;
 88     m = read();
 89     for (int i=1; i<=m; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
 90     std::sort(q+1, q+m+1);
 91     lbd = 1, rbd = 0;
 92     for (int i=1; i<=m; i++)
 93     {
 94         while (lbd < q[i].l) lErase(lbd++);
 95         while (lbd > q[i].l) lAdd(--lbd);
 96         while (rbd > q[i].r) rErase(rbd--);
 97         while (rbd < q[i].r) rAdd(++rbd);
 98         ans[q[i].id] = tot;
 99     }
100     for (int i=1; i<=m; i++) printf("%d\n",ans[i]);
101     return 0;
102 }

 

 

 

 

END

posted @ 2018-10-06 21:45  AntiQuality  阅读(108)  评论(0编辑  收藏  举报