BZOJ4542: [Hnoi2016]大数

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 2209  Solved: 839

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

/**************************************************************

    Problem: 4542

    User: ictsing

    Language: C++

    Result: Accepted

    Time:4988 ms

    Memory:9652 kb

****************************************************************/

 

/*

多次询问区间,并不强制在线,可以考虑莫队算法。

  

首先有性质,如果一个数的后缀[r,n],[l,n]modp相等,

那么[l,r-1]是p的倍数。

我们来证明一下这个:

[r,n]=t'

[l,r-1]=t

那么[l,n]=t*10^(n-r+1)+t'=w

假设t'%p=x,w%p=x

那么显然t%p=x

?!

如果p是10的因数,即p=2或5,

那么这个结论就是错的,因为无论怎么取都是相等的。

那么p=2或5的时候特判一下就好了。

怎么特判呢?

我们维护一个前缀和,如果这个前缀是2或5的倍数那么前缀加,

再开一个计数的,表示这个前缀可以和几个组合。

然后再考虑其他情况怎么做。

我们预处理出num[i],表示[i,n]modp的余数。

然后开一个cnt[]来记录每个数在当前区间出现次数。

但是注意到p可能非常的大,我们想办法离散化一下。

我们把得到的所有的num[i]sort一遍。

每个值映射到他出现的第一个位置上,

那么这个cnt[]最大也就只是n了。

*/

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cmath>

#include <cstring>

#include <map>

using namespace std;

typedef long long ll;

const ll mt = 100000+5;

inline ll readll()

{

    ll num=0,flag=1;

    char ch;

    do{

      ch=getchar();

      if(ch=='-') flag=-1;

    }while(ch<'0'||ch>'9');

    do{

       num=num*10+ch-'0';

       ch=getchar();

    }while(ch>='0'&&ch<='9');

    return num*flag;

}

ll sz;

char s[mt];

struct ques

{

    ll l,r,id;

}q[mt];

ll ans_[mt];

ll num[mt],qaq[mt];

bool cmp(ques a,ques b)

{

    if((a.l-1)/sz==(b.l-1)/sz) return a.r<b.r;

    return (a.l-1)/sz<(b.l-1)/sz;

}

ll cnt[mt];

map<ll,ll>map_;

int main()

{

    ll p=readll();

    scanf("%s",s+1);

    ll len=strlen(s+1);

    ll m=readll();

    if(p!=2&&p!=5)

    {

        sz=sqrt(len);

        ll number=0;

        ll bas=1;

        for(ll i=len;i>=1;i--)

        {

            number=(number+(s[i]-'0')*bas)%p;

            bas=bas*10%p;

            qaq[i]=num[i]=number;

            //cout<<qaq[i]<<endl;

        }

        sort(qaq+1,qaq+len+1);

        for(ll i=1;i<=len+1;i++)

            if(!map_[qaq[i]]) map_[qaq[i]]=i;

        for(ll i=1;i<=len+1;i++)

            num[i]=map_[num[i]];

        for(ll i=1;i<=m;i++)

        {

            q[i].l=readll(),q[i].r=readll(),q[i].id=i;

            q[i].r++;

        }

        sort(q+1,q+m+1,cmp);

        ll L=1,R=0,ans=0;

        for(ll i=1;i<=m;i++)

        {

            while(R<q[i].r) R++,ans+=cnt[num[R]],cnt[num[R]]++;

            while(L>q[i].l) L--,ans+=cnt[num[L]],cnt[num[L]]++;

            while(L<q[i].l) cnt[num[L]]--,ans-=cnt[num[L]],L++;

            while(R>q[i].r) cnt[num[R]]--,ans-=cnt[num[R]],R--;

            ans_[q[i].id]=ans;

        }

        for(ll i=1;i<=m;i++)

            printf("%lld\n",ans_[i]);

    }

    else

    {

        ll number=0;

        for(ll i=1;i<=len;i++)

        {

            number=number+s[i]-'0';

            if(!(number%p))

                num[i]=num[i-1]+1,cnt[i]=cnt[i-1]+i;

            else num[i]=num[i-1],cnt[i]=cnt[i-1];

            number=number*10;

        }

        for(ll i=1;i<=m;i++)

        {

            ll l=readll(),r=readll();

            ll ans=cnt[r]-cnt[l-1]-(num[r]-num[l-1])*(l-1);

            printf("%lld\n",ans);

        }

    }

    return 0;

}

 


评论

热度(3)