zoukankan      html  css  js  c++  java
  • 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

    Sample Output

    5
    3
    2
    //第一个询问问的是整个串,满足条件的子串分别有:121121,2112,11,121,121。

    HINT

     2016.4.19新加数据一组


    题解Here!
    话说$AHOI2016DAY2$一天考了两次莫队,这是要上天的节奏啊。。。
    当然对于我这个只会大力$DS$的蒟蒻应该是一件好事情。。。
    又遇到了区间询问,还没有强制在线。
    当然拿出区间神器——莫队
    但是新加了一组数据$p==2||p==5$,我们只好分类讨论一下:
    1. $p==2||p==5$:用$front[i]$表示$i$的前缀有多少个可以被$p$整除的子串,$sum[i]$表示有多少个可以被$p$整除的数。
    求区间多少个子串的时候用$front[r]-front[l-1]-( ext{[1,l-1]中整除p的数对区间l-r的贡献})$。
    2. 把$n$个后缀组成的数字全部对$p$取模。
    若$s[l,n]$的余数和$s[r,n]$的余数相同,那么$s[l,r–1]$区间内的数字就是$p$的倍数。
    然后这个题就变成经典莫队题了:
    给定一个序列,每次询问$[l, r]$内有多少对相同的数
    每一个余数$s[i]$给一个计数器$num[i]$(需离散化),记录$[l, r]$中这个数出现了几次。
    区间长度改变时答案改变值为$num[i]$。
    注:记得开$long long$。。。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<map>
    #include<cmath>
    #include<cstring>
    #define MAXN 100010
    using namespace std;
    map<long long,long long> ranks;
    int n,m;
    long long p,val[MAXN];
    long long front[MAXN],ans[MAXN],sum[MAXN],b[MAXN],num[MAXN];
    struct node{
        int l,r,id;
    }que[MAXN];
    inline long long read(){
        long long date=0,w=1;char c=0;
        while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
        while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
        return date*w;
    }
    bool cmp1(const node &x,const node &y){
        return x.l<y.l;
    }
    bool cmp2(const node &x,const node &y){
        if(x.r==y.r)return x.l<y.l;
        return x.r<y.r;
    }
    void work1(){
        for(int i=1;i<=n;i++){
            front[i]=front[i-1];sum[i]=sum[i-1];
            if(val[i]%p==0){
                front[i]+=i;
                sum[i]++;
            }
        }
        while(m--){
            int l=read(),r=read();
            printf("%lld
    ",front[r]-front[l-1]-(sum[r]-sum[l-1])*(l-1));
        }
    }
    void work2(){
        int nowi=1,nowj=0,x,d,left=1,right=0;
        long long s;
        for(int i=1;i<=m;i++){
            que[i].l=read();que[i].r=read();
            que[i].id=i;
        }
        x=sqrt(n);
        sort(que+1,que+m+1,cmp1);
        while(nowi<=m){
            nowj++;
            d=nowi;
            while(que[nowi].l<nowj*x&&nowi<=m)nowi++;
            sort(que+d,que+nowi,cmp2);
            if(nowj==x){
                sort(que+d,que+m+1,cmp2);
                break;
            }
        }
        s=1;
        for(int i=n;i>=1;i--,s=s*10%p)b[i]=sum[i]=(val[i]*s%p+sum[i+1])%p;
        sort(b+1,b+n+2);
        for(int i=1;i<=n+1;i++)ranks[b[i]]=i;
        for(int i=1;i<=n+1;i++)sum[i]=ranks[sum[i]];
        s=0;
        for(int i=1;i<=m;i++){
            while(que[i].l<left)
            {
                left--;
                s+=num[sum[left]];
                num[sum[left]]++;
            }
            while(que[i].l>left)
            {
                s-=num[sum[left]]-1;
                num[sum[left]]--;
                left++;
            }
            while(right<que[i].r+1)
            {
                right++;
                s+=num[sum[right]];
                num[sum[right]]++;
            }
            while(right>que[i].r+1)
            {
                s-=num[sum[right]]-1;
                num[sum[right]]--;
                right--;
            }
            ans[que[i].id]=s;
        }
        for(int i=1;i<=m;i++)printf("%lld
    ",ans[i]);
    }
    void init(){
        char ch[MAXN];
        p=read();scanf("%s",ch);m=read();
        n=strlen(ch);
        for(int i=0;i<n;i++)val[i+1]=ch[i]-'0';
    }
    int main(){
        init();
        if(p==2||p==5)work1();
        else work2();
        return 0;
    }
    
  • 相关阅读:
    机器学习(Machine Learning)&深入学习(Deep Learning)资料
    漫谈 机器学习
    Android 屏幕滑动事件
    Andriod中绘(画)图----Canvas的使用详解
    android studio上代码编译调试中遇到的一些异常记录
    Android签名详解(debug和release)
    如何用AndroidStudio导入github项目
    java synchronized详解
    视频编解码学习之一:理论基础
    Android 环境下编译FFmpeg
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9460409.html
Copyright © 2011-2022 走看看