洛谷P3245 大数 [HNOI2016] 莫队

正解:莫队

解题报告:

传送门

这题首先要发现一个结论,是这样儿的:

若p不是10的约数(即2和5) 时,当第i位到第n位组成的数%p==第j位到第n位组成的数%p,那么第i位到第j位上的数组成的数%p=0

试证如下:

记后缀数组num[i]:第i位到第n位构成的数,dat[i,j]:第i位到第j位构成的数

那么可以表示出dat[i,j-1]=(num[j]-num[i])*10j-i

因为现在已知num[i]=num[j]了,所以dat[i,j-1]=0

(显然指的是%p意义下昂QAQ

所以可以先预处理出后缀数组,于是问题就变成了这样:

有一些询问,每个询问给定一个区间,求区间内相同元素的数量

这不就是,莫队经典板子题了嘛,,,然后就做完了QAQ

然后以上都是我们在p不是10的约数的情况下讨论的(因为当p是10的约数时可能有更多QAQ

但是考虑到p是10的约数就只可能p=2或p=5嘛,然后2和5的倍数的性质又特别明显——尾数484倍数,所以直接前缀和就好辣QAQ

具体等下放代码QAQ

然后关于实现,并不很难和莫队普通题都差不多

唯一一个要注意的是这样儿的:

对于[l,r],因为计算的是[l,r]之间的数,所以实际上的那个num是要算到r+1的,但是同时要注意的是,r+1是不能计入数量的,而且在统计完答案之后还要把ans中的关于r+1多计算的减回来

但是想到这儿还要想到一个细节

就是当给定的r到达最右端的时候,如果依然按照上面的套路来会有问题,因为那样加上的会是0的数量,但是在离散化之后最小的数都是1

所以认真想想可以发现最右端的右边的贡献其实就是0的贡献,然后就想到怎么求0的离散化之后的值,显然在排序之后要么就是麻油0要么就是0离散化之后=1,分类讨论一下就好

表述不太清晰感觉,,,所以单独放一下这段的代码趴QAQ

(然后其实我好像做复杂了,,,因为我看到好像别人都麻油分类讨论什么的,,,直接一视同仁地做过去就欧克了,,,估计是前面有什么处理不一样,但是我jio得也就多打几句话而已,所以就懒得再修改了QAQ

if(r!=str_lth-1)ret+=num[dat[r+1]];else if(st[1]==0)ret+=num[1];
as[ques[i].id]=ret;if(r!=str_lth-1)ret-=num[dat[r+1]];else if(st[1]==0)ret-=num[1];
其实也就两句而已QAQ
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define gc getchar()
#define ll unsigned long long
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define my(i,x,y) for(rg ll i=x;i>=y;--i)

const ll M=100000+10,N=100000+10;
ll p,m,str_lth,sq_lth,dat[N],st[N],tot,num[N],as[M],sum[N];
string str;
struct qs{ll l,r,id;}ques[M];

il ll read()
{
    rg char ch=gc;rg ll x=0;rg bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il bool cmp(qs gd,qs gs){
     
    
    return gd.r/sq_lth==gs.r/sq_lth?gd.l<gs.l:gd.r<gs.r;}
il void wk1()
{
    string str;cin>>str;str=' '+str;str_lth=str.length();sq_lth=sqrt(str_lth);ll tmp=1;
    my(i,str_lth-1,1)st[i]=dat[i]=(dat[i+1]+tmp*(str[i]^'0'))%p,tmp=tmp*10%p;
    sort(st+1,st+str_lth+1);tot=unique(st+1,st+str_lth+1)-st;rp(i,1,str_lth-1)dat[i]=lower_bound(st+1,st+tot,dat[i])-st;
    m=read();rp(i,1,m)ques[i]=(qs){read(),read(),i};sort(ques+1,ques+1+m,cmp);ll ret=0,l=ques[1].r+1,r=ques[1].r;
    rp(i,1,m)
    {
        while(l>ques[i].l)ret+=num[dat[--l]]++;
        while(r<ques[i].r)ret+=num[dat[++r]]++;
        while(l<ques[i].l)ret-=--num[dat[l++]];
        while(r>ques[i].r)ret-=--num[dat[r--]];
        if(r!=str_lth-1)ret+=num[dat[r+1]];else if(st[1]==0)ret+=num[1];
        as[ques[i].id]=ret;if(r!=str_lth-1)ret-=num[dat[r+1]];else if(st[1]==0)ret-=num[1];
    }
    rp(i,1,m)if(as[i])printf("%lld\n",as[i]);else printf("0\n");
}
il void wk2()
{
    string str;cin>>str;str=' '+str;str_lth=str.length();m=read();rp(i,1,m)ques[i]=(qs){read(),read(),i};
    rp(i,1,str_lth)dat[i]=(str[i]^'0')%p,st[i]=st[i-1]+(dat[i]==0),sum[i]=sum[i-1]+(dat[i]==0)*i;
    rp(i,1,m)printf("%lld\n",sum[ques[i].r]-sum[ques[i].l-1]-(ques[i].l-1)*(st[ques[i].r]-st[ques[i].l-1]));
}

int main()
{
    p=read();if(p!=2 && p!=5)wk1();else wk2();
    return 0;
}
最后放下这个题目的代码!