bzoj 4542 洛谷 3245 [HNOI]2016大数 题解

博客观赏效果更佳

题意简述

给你一个数字字符串 S S ,长度为 n < = 1 e 5 n<=1e5 。有一个 i n t int 范围内的质数 P P Q < = 1 e 5 Q<=1e5 次询问,每次询问一个区间 [ l , r ] [l,r] 中, S S 的多少个子串(不一定不同)是 P P 的倍数。

比如 S = " 007 " S="007" ,它有6个子串,分别是 { 0 , 0 , 00 , 07 , 007 } \{0,0,00,07,007\} ,这 6 6 个都是 7 7 的倍数。

思路框架

处理后缀和,分类讨论 p p 10 10 是否互质。一个用莫队维护,一个用树状数组维护。

具体思路

先分类讨论。

1. P和10互质

那么末尾多几个 0 0 就不会影响对 P P 的整除性。

维护后缀和 s u f suf ,它表示从 i i 开始的后缀接起来膜 P P 的值。特殊地, s u f [ n + 1 ] = 0 suf[n+1]=0 。对于一个区间 [ l , r ] [l,r] ,当 s u f [ l ] suf[l] s u f [ r + 1 ] suf[r+1] 相等的时候, [ l , r ] [l,r] 这一段就是 P P 的倍数了。相当于我们要在 s u f suf 数组中维护相等的无序对数,莫队即珂。

2. P不和10互质

即:P=2或5

这个时候我们就只需要判断末尾就好了。对于 2 2 的情况,那么子串结尾就是 0 , 2 , 4 , 6 , 8 0,2,4,6,8 。对于 5 5 的情况,子串结尾就是 0 , 5 0,5

假设询问 [ l , r ] [l,r] 。我们找到一个 k k ,如果以 k k 为结尾的子串一定满足条件,那么这个 k k 对答案的贡献就是 k ( l 1 ) k-(l-1) 。(因为开始位置珂以是 [ l , k ] [l,k] 中的任意一个,一共有 k ( l 1 ) k-(l-1) 种情况)

我们先对 2 2 5 5 的情况分开讨论,然后开 20 20 个树状数组,其中 10 10 个维护长度为0~9的下标的和,另外 10 10 个维护 0   9 0~9 中的数量和。

扫描二维码关注公众号,回复: 8528897 查看本文章

对于 [ l , r ] [l,r] 的询问,相当于要求和所有 k ( l 1 ) k-(l-1) 。令满足条件 k k 的数量是 C C ,和为 S S 。那么这一段答案就是 S ( l 1 ) × C S-(l-1)\times C 。对于每一种尾数累加答案即珂。

代码


#include <bits/stdc++.h>using namespace std;
namespace Flandre_Scarlet
{
    #define N 155555
    #define int long long 
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)

    int n,p;
    char s[N];
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(p);scanf("%s",s+1);
        n=strlen(s+1);
    }

    
    int a[N];
    namespace SpecialCxk //p=2或5的情况
    {
        class BIT
        {
            public:
                int tree[N];
                int len;
                void BuildTree(int _len)
                {
                    len=_len;
                    FK(tree);
                }
                void Add(int pos,int val=1)
                {
                    for(int i=pos;i<=len;i+=(i&(-i)))
                    {
                        tree[i]+=val;
                    }
                }
                int Query(int pos)
                {
                    int ans=0;
                    for(int i=pos;i>0;i-=(i&(-i)))
                    {
                        ans+=tree[i];
                    }
                    return ans;
                }
                int RQuery(int l,int r)
                {
                    return Query(r)-Query(l-1);
                }
        }T[10][2]; //T[i][j]: 值为i的所有位置的j次方和
        //换句话说,j=0时计算数量,j=1时计算下标的和

        void Soviet()
        {
            F(i,0,9) F(j,0,1) T[i][j].BuildTree(n+5);

            if (p==2)
            {
                F(i,1,n)
                {
                    T[a[i]][0].Add(i,1);
                    T[a[i]][1].Add(i,i); //对于第i个位置,一个加上1,一个加上i
                    //所以一个是数量和,一个是下标和
                }
                int q;R1(q);
                F(i,1,q)
                {
                    int l,r;
                    R1(l),R1(r);
                    int cnt0=T[0][0].RQuery(l,r);
                    int sum0=T[0][1].RQuery(l,r)-(l-1)*cnt0;

                    int cnt2=T[2][0].RQuery(l,r);
                    int sum2=T[2][1].RQuery(l,r)-(l-1)*cnt2;

                    int cnt4=T[4][0].RQuery(l,r);
                    int sum4=T[4][1].RQuery(l,r)-(l-1)*cnt4;

                    int cnt8=T[8][0].RQuery(l,r);
                    int sum8=T[8][1].RQuery(l,r)-(l-1)*cnt8;
                    //0 2 4 8分类讨论一下
                    printf("%lld\n",sum0+sum2+sum4+sum8); //累加
                }
            }
            else if (p==5) //和p==2的情况没什么本质差别,是同样的思路
            {
                F(i,1,n)
                {
                    T[a[i]][0].Add(i,1);
                    T[a[i]][1].Add(i,i);
                }
                int q;R1(q);
                F(i,1,q)
                {
                    int l,r;
                    R1(l),R1(r);
                    int cnt0=T[0][0].RQuery(l,r);
                    int sum0=T[0][1].RQuery(l,r)-(l-1)*cnt0;

                    int cnt5=T[5][0].RQuery(l,r);
                    int sum5=T[5][1].RQuery(l,r)-(l-1)*cnt5;
                    printf("%lld\n",sum0+sum5);
                }
            }
        }
    }

    namespace Normal //p不是2或5的情况
    {
        int p10[N],suf[N];
        map<int,int> disc;int dcnt=0;

        int q,sn,ans[N];
        struct node{int l,r,id;}Q[N]; 
        bool cmp(node a,node b){return a.l/sn<b.l/sn or (a.l/sn==b.l/sn and a.r<b.r);}
        
        int cur;
        int cnt[N];
        void Add(int x){cur+=cnt[x];cnt[x]++;}
        void Del(int x){cnt[x]--;cur-=cnt[x];}
        void Soviet()
        {
            p10[0]=1;F(i,1,n) p10[i]=p10[i-1]*10ll%p;  //处理10的幂,方便维护suf数组
            suf[n+1]=0; D(i,n,1) suf[i]=(suf[i+1]+p10[n-i]*a[i])%p; //求出后缀和数组suf
            F(i,1,n+1)
            {
                if (!disc[suf[i]]) disc[suf[i]]=++dcnt;
                suf[i]=disc[suf[i]];
            } //把suf离散化,我用的是map离散化,懒得写排序+lowerbound了
            //反正map也能过

            sn=sqrt(n+0.5); 
            R1(q);
            F(i,1,q)
            {
                int l,r;R1(l),R1(r);
                Q[i]=(node){l,r+1,i}; //注意这里改成l,r+1
            }
            sort(Q+1,Q+q+1,cmp);
            int ll=1,rr=0;cur=0;
            F(i,1,q)
            {
                while(ll<Q[i].l) Del(suf[ll]),ll++;
                while(rr>Q[i].r) Del(suf[rr]),rr--;
                while(ll>Q[i].l) ll--,Add(suf[ll]);
                while(rr<Q[i].r) rr++,Add(suf[rr]);

                ans[Q[i].id]=cur;
            } //莫队
            F(i,1,q) printf("%lld\n",ans[i]);
        }
    }
    void Soviet()
    {
        F(i,1,n) a[i]=s[i]-'0';
        if (p==2 or p==5) SpecialCxk::Soviet();
        else Normal::Soviet();
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
    #undef int //long long 
}
int main(){
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
发布了210 篇原创文章 · 获赞 8 · 访问量 8972

猜你喜欢

转载自blog.csdn.net/LightningUZ/article/details/103758080
今日推荐