【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个询问的答案。

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新加数据一组

题解
莫队,注意p=2,5

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
struct node1{int l,r,block,id;}a[100005];
struct node2{int v,id;}b[100005];
int c[100005],p,n,m,size,sum[100005],aa[100005],s1[100005],s2[1000005];
long long num[100005];
char ch[100005];
bool cmp(node1 a,node1 b)
{
    if (a.block==b.block) return a.r<b.r;
    return a.block<b.block;
}
bool cmp_ls(node2 a,node2 b)
{
    return a.v<b.v;
}
int read()
{
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9'){ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
using namespace std;
int main()
{
    p=read();
    scanf("%s",ch+1);
    n=strlen(ch+1);
    if (p==2 || p==5)
    {
        for (int i=1;i<=n;++i) 
        {
            aa[i]=ch[i]-'0';if (aa[i]%p==0) s1[i]=s1[i-1]+1,s2[i]=s2[i-1]+i;else s1[i]=s1[i-1],s2[i]=s2[i-1];
        }
        int m;
        cin>>m;
        while (m--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%lld\n",(ll)(s2[y])-s2[x-1]-((ll)(x)-1)*((ll)(s1[y])-s1[x-1]));
        }
        return 0;
    }
    int s=0;ll len=1;
    for (int i=n;i>=1;i--)
    {
        s=(s+(ch[i]-'0')*len)%p;
        len=(len*10)%p;
        b[i].id=i;
        b[i].v=s;
    }
    sort(b+1,b+n+1,cmp_ls);
    int pre=0,li=0;
    for (int i=0;i<=n;i++)
    {
        if (b[i].v!=pre) pre=b[i].v,li++;
        c[b[i].id]=li;
    }
    m=read();
    size=sqrt(n);
    for (int i=1;i<=m;i++)
    {
        a[i].l=read();a[i].r=read();
        a[i].block=(a[i].l-1)/size+1;
        a[i].id=i;
    }
    sort(a+1,a+m+1,cmp);
    int l=1,r=0;long long ans=0;
    for (int i=1;i<=m;i++)
    {
        while (l<a[i].l){ans-=sum[c[l]]-1;sum[c[l]]--;l++;}
        while (l>a[i].l){l--;sum[c[l]]++;ans+=sum[c[l]]-1;}
        while (r<=a[i].r){r++;sum[c[r]]++;ans+=sum[c[r]]-1;}
        while (r>a[i].r+1){ans-=sum[c[r]]-1;sum[c[r]]--;r--;}
        num[a[i].id]=ans;
    }
    for (int i=1;i<=m;i++)
        printf("%lld\n",num[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值