BZOJ 4542([Hnoi2016]序列-莫队)

本文介绍了一种算法,用于解决给定一个很大的数S和一个素数P时,如何快速查询S的子串中是P的倍数的子串数量。提供了针对不同素数的解决方案,并使用莫队算法进行优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

如果gcd(p,10)=1,显然一个子串是k的倍数当且仅当它的2个后缀mod p余数相同。
当p是2,5的时候可以根据最后一位判断。
这2种情况都可以用莫队。

#include<bits/stdc++.h> 
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
                        For(j,m-1) cout<<a[i][j]<<' ';\
                        cout<<a[i][m]<<endl; \
                        } 
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
#define MAXN (100010)
ll b[MAXN],res[MAXN],belong[MAXN],cnt[MAXN]={};
struct node{
    int l,r,id;
    friend bool operator<(node a,node b) {
        if (belong[a.l] ^ belong[b.l] )
            return belong[a.l] < belong[b.l];
        return a.r<b.r; 
    }
}a[MAXN];
char s[MAXN];
ll c[MAXN];
map<ll,int> h;
int main()
{
//  freopen("bzoj4542.in","r",stdin);
//  freopen(".out","w",stdout);

    ll p;cin>>p;
    scanf("%s",s+1);
    int n=strlen(s+1);
    int m=read();
    int BS=sqrt(n);

    For(i,m){
        a[i]=node{read(),read(),i};
    }

    For(i,n)
        belong[i]=(i-1)/BS+1;
    int l=1,r=0;

    if (p==2||p==5) {
        sort(a+1,a+m+1);
        ll t=0,ans=0;
        For(i,m){
            while(l>a[i].l){
                l--,t+=((s[l]-'0')%p)==0,ans+=t;
            }while(r<a[i].r){
                r++;
                if (((s[r]-'0')%p)==0) {
                    t++;
                    ans+=r-l+1; 
                }
            }while(l<a[i].l){
                if (((s[l]-'0')%p)==0) {
                    ans-=t; t--;
                } else {
                    ans-=t;
                }
                l++;
            }while(r>a[i].r){
                if (((s[r]-'0')%p)==0) {
                    ans-=r-l+1; 
                    t--;
                }
                r--;
            }
            res[a[i].id]=ans;
        }


    }
    else {
        ll t=0,c10=1;
        ForD(i,n) {
            t=(t+c10*(s[i]-'0'))%p;
            c[i]=t;
            c10=(c10*10)%p;
        }
        c[n+1]=0;

        For(i,n+1) h[c[i]]=1;
        int pid=0;
        for(map<ll,int>::iterator it=h.begin();it!=h.end();it++) {
            it->se=++pid;
//          cout<<it->se<<endl;
        }
//      For(i,n) cout<<c[i]<<' '<<h[c[i]]<<endl;
        For(i,n+1) c[i]=h[c[i]];


        For(i,n) a[i].r++;
        sort(a+1,a+m+1);

        ll ans=0;
        MEM(cnt)
        For(i,m){
            while(l>a[i].l){
                --l;
                ans+=cnt[c[l]]++;
            }while(r<a[i].r){
                ++r;
                ans+=cnt[c[r]]++;
            }while(l<a[i].l){
                ans-=--cnt[c[l]];
                l++;
            }while(r>a[i].r){
                ans-=--cnt[c[r]];
                r--;
            }
            res[a[i].id]=ans;
        }
    }
    For(i,m)
        printf("%lld\n",res[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值