• 【bzoj4542】[Hnoi2016]大数 莫队算法


    题目描述

    给出一个数字串,多次询问一段区间有多少个子区间对应的数为P的倍数。其中P为质数。

    输入

    第一行一个整数: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为素数

    输出

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

    样例输入

    11
    121121
    3
    1 6
    1 5
    1 4

    样例输出

    5
    3
    2


    题解

    莫队算法

    设 $b[i]=S[i...n] ext{mod} p$ ,那么 $S[l,r]$ 为 $p$ 的倍数,当且仅当: $frac{b[l]-b[r+1]}{10^{r-l}} ext{mod} p=0$ 。

    当 $p=2$ 或 $p=5$ 时,直接通过数字串末位判断是不是 $p$ 的倍数。设 $v[i]=[i ext{mod} p=0]$ ,维护 $v[i]$ 和 $v[i]·i$ 的前缀和即可快速得出答案。

    当 $p eq 2$ 且 $p eq 5$ 时,$p$ 与 $10$ 互质。此时条件简化为:$b[l]equiv b[r+1] ( ext{mod} p)$ 。

    因此原问题转化为:求 $[l,r+1]$ 内 $b$ 值相等的数对的数目。

    将 $b$ 离散化,使用莫队算法,用桶维护离散化后的某个 $b$ 的出现次数,指针移动时统计答案即可。

    时间复杂度 $O(nsqrt n)$ 

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 100010
    using namespace std;
    typedef long long ll;
    ll p;
    char str[N];
    namespace task1
    {
        ll s1[N] , s2[N];
        void solve()
        {
            int n , m , i , l , r;
            scanf("%s%d" , str + 1 , &m) , n = strlen(str + 1);
            for(i = 1 ; i <= n ; i ++ )
            {
                s1[i] = s1[i - 1] , s2[i] = s2[i - 1];
                if((str[i] - '0') % p == 0) s1[i] ++ , s2[i] += i;
            }
            while(m -- )
            {
                scanf("%d%d" , &l , &r);
                printf("%lld
    " , s2[r] - s2[l - 1] - (l - 1) * (s1[r] - s1[l - 1]));
            }
        }
    }
    namespace task2
    {
        struct data
        {
            int l , r , bl , id;
            bool operator<(const data &a)const {return bl == a.bl ? r < a.r : bl < a.bl;}
        }q[N];
        int a[N] , mp[N];
        ll b[N] , v[N] , ans[N];
        void solve()
        {
            int n , m , i , si , lp = 1 , rp = 0;
            ll t = 1 , now = 0;
            scanf("%s%d" , str + 1 , &m) , n = strlen(str + 1) , si = (int)sqrt(n);
            for(i = n ; i ; i -- , t = t * 10 % p) v[i] = b[i] = (b[i + 1] + (str[i] - '0') * t) % p;
            v[n + 1] = 0 , sort(v + 1 , v + n + 2);
            for(i = 1 ; i <= n + 1 ; i ++ ) a[i] = lower_bound(v + 1 , v + n + 2 , b[i]) - v;
            for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &q[i].l , &q[i].r) , q[i].r ++ , q[i].bl = (q[i].l - 1) / si , q[i].id = i;
            sort(q + 1 , q + m + 1);
            for(i = 1 ; i <= m ; i ++ )
            {
                while(lp > q[i].l) now += mp[a[--lp]] , mp[a[lp]] ++ ;
                while(rp < q[i].r) now += mp[a[++rp]] , mp[a[rp]] ++ ;
                while(lp < q[i].l) mp[a[lp]] -- , now -= mp[a[lp++]];
                while(rp > q[i].r) mp[a[rp]] -- , now -= mp[a[rp--]];
                ans[q[i].id] = now;
            }
            for(i = 1 ; i <= m ; i ++ ) printf("%lld
    " , ans[i]);
        }
    }
    int main()
    {
        scanf("%lld" , &p);
        if(p == 2 || p == 5) task1::solve();
        else task2::solve();
        return 0;
    }
    
  • 相关阅读:
    loadrunner -27778 https连接问题
    https调试
    Session Alerts
    Pause Web Sessions
    Customize Web Sessions List
    单例模式:Java单例模式的几种写法及它们的优缺点
    Activity: launchMode 和 Intent.FLAG_ACTIVITY_CLEAR_TOP
    TextView: android:ellipsize="marquee" 跑马灯效果无效的问题
    Socket通信(1):搭建开发环境
    linux: QT安装时出现段错误segmentation fault
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8597338.html
Copyright © 2020-2023  润新知