[HNOI2016] 大数 题解
[HNOI2016] 大数 题解
题意简述
给定一个质模数 \(p\),一个数字串 \(S\),以及 \(m\) 个询问 l,r:问 \(S\) 在 \([l,r]\) 间的子串有几个组成的数字能被 \(p\) 整除。
分析
首先明显的要我们做一个类似字符串 Hash 的前缀操作,设 \(pre_i\) 表示 \(S[1,i] \bmod p\) 的值,特别的 \(pre_0=0\)。
那么 \(S[l,r]\) 能被 \(p\) 整除的条件就是:\(pre_r - 10^{r-(l-1)}pre_{l-1} \equiv 0 \pmod p\)。
我们可以离线下来然后处理,拿到 \(n\le 10000,m\le 1000\) 的分数。
我们尝试进行变换:
\[\begin{aligned}
pre_r - 10^{r-(l-1)}pre_{l-1} & \equiv 0 \pmod p \\
(pre_r - 10^{r-n} 10^{n-(l-1)}pre_{l-1}) & \equiv 0 \pmod p \\
10^{r-n}(10^{n-r}pre_r - 10^{n-(l-1)}pre_{l-1}) & \equiv 0 \pmod p \\
\end{aligned}
\]
那么可以得到:\(10^{r-n} \equiv 0 \pmod p\) 或 \((10^{n-r}pre_r - 10^{n-(l-1)}pre_{l-1}) \equiv 0 \pmod p\)。
首先对于 \(p=2,5\) 的情况特殊处理,然后剩下的一定是 \((10^{n-r}pre_r - 10^{n-(l-1)}pre_{l-1}) \equiv 0 \pmod p\)。
那么设 \(Pre_i = 10^{n-i}pre_i \bmod p\),能够构成整除区间的 \(l,r\) 一定满足:\(Pre_{l-1}=Pre_{r}\)。
那么我们就可以套上莫队了。
代码
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define FOR(i,a,b) for(int i(a); i<=(int)(b); ++i)
#define DOR(i,a,b) for(int i(a); i>=(int)(b); --i)
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v); ~i; y=g[i=g[i].nxt].v)
using namespace std;
constexpr int N(2e5+10);
namespace IOEcat {
#define isD(c) ('0'<=(c)&&(c)<='9')
#define DE(...) E(#__VA_ARGS__,__VA_ARGS__)
struct Icat {
char getc() {
return getchar();
}
template<class T>void operator ()(T &x) {
static bool sign(0);
static char ch(0);
sign=0,x=0;
while(ch=getc(),!isD(ch))if(ch=='-')sign=1;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getc(),isD(ch));
if(sign)x=-x;
}
template<class T,class...Types>void operator ()(T &x,Types&...args) {
return (*this)(x),(*this)(args...);
}
} I;
struct Ocat {
void putc(char c) {
putchar(c);
}
template<class T>void operator ()(T x,const char lst='\n') {
static int top(0);
static char st[100];
if(x<0)x=-x,putc('-');
do st[++top]=(x%10)^48,x/=10;
while(x);
while(top)putc(st[top--]);
putc(lst);
}
template<class T,class...Types>void operator ()(const T x,const char lst='\n',const Types...args) {
return (*this)(x,lst),(*this)(args...);
}
} O;
struct Ecat {
template<class T>void operator ()(const char *fmt,const T x) {
cerr<<fmt<<':'<<x<<'.'<<endl;
}
template<class T,class...Types>void operator ()(const char *fmt,const T x,const Types...args) {
while(*fmt^',')cerr<<*fmt++;
return cerr<<':'<<x<<" ,",(*this)(++fmt,args...);
}
} E;
} using namespace IOEcat;
namespace Modular {
int p;
int pw[N];
template<class T1,class T2>auto add(const T1 a,const T2 b) {
return a+b>=p?a+b-p:a+b;
}
template<class T1,class T2>auto mul(const T1 a,const T2 b) {
return (ll)a*b%p;
}
template<class T1,class T2>T1 &toadd(T1 &a,const T2 b) {
return a=add(a,b);
}
template<class T1,class T2>T1 &tomul(T1 &a,const T2 b) {
return a=mul(a,b);
}
void Init(int n=N-5) {
FOR(i,pw[0]=1,n)pw[i]=mul(pw[i-1],10);
}
} using namespace Modular;
char S[N];
int n,Q;
int a[N],ans[N],pre[N];
struct Query {
int l,r,id;
} Que[N];
namespace Subtask2 {
int Suf[N];
bool Check() {
return n<=10000&&Q<=1000;
}
int Cmain() {
int it(1);
sort(Que+1,Que+Q+1,[](Query a,Query b) { return a.r<b.r; });
FOR(r,1,n) {
int sum(0);
DOR(l,r,1) {
if(pre[r]==mul(pw[r-l+1],pre[l-1]))++sum;
Suf[l]+=sum;
}
while(it<=Q&&Que[it].r<=r)ans[Que[it].id]=Suf[Que[it].l],++it;
}
FOR(i,1,Q)O(ans[i],'\n');
return 0;
}
}
namespace Subtask_Spe {
int cnt[N];
ll pre[N];
bool Check() {
return p==2||p==5;
}
int Cmain() {
FOR(i,1,n)pre[i]=pre[i-1]+(a[i]%p==0?i:0),cnt[i]=cnt[i-1]+(a[i]%p==0);
FOR(i,1,Q)O((pre[Que[i].r]-pre[Que[i].l-1])-(ll)(cnt[Que[i].r]-cnt[Que[i].l-1])*(Que[i].l-1),'\n');
return 0;
}
}
namespace Subtask {
int Bl,Bn;
int b[N],id[N],cnt[N];
ll sum;
int Cmain() {
++n,Bl=ceil(1.5*n/sqrt(Q)),Bn=(n-1)/Bl+1;
FOR(i,1,Bn)FOR(j,Bl*(i-1)+1,min(n,Bl*i))id[j]=i;
DOR(i,n,1)pre[i]=mul(pre[i-1],pw[n-i]),b[++b[0]]=pre[i];
sort(b+1,b+b[0]+1),b[0]=unique(b+1,b+b[0]+1)-b-1;
FOR(i,1,n)pre[i]=lower_bound(b+1,b+b[0]+1,pre[i])-b;
FOR(i,1,Q)++Que[i].r;
sort(Que+1,Que+Q+1,[](Query a,Query b) {
return id[a.l]^id[b.l]?id[a.l]<id[b.l]:(id[a.l]&1?a.r<b.r:a.r>b.r);
});
int l(1),r(0);
FOR(i,1,Q) {
while(r<Que[i].r)sum+=cnt[pre[++r]],++cnt[pre[r]];
while(l>Que[i].l)sum+=cnt[pre[--l]],++cnt[pre[l]];
while(r>Que[i].r)--cnt[pre[r]],sum-=cnt[pre[r--]];
while(l<Que[i].l)--cnt[pre[l]],sum-=cnt[pre[l++]];
ans[Que[i].id]=sum;
}
FOR(i,1,Q)O(ans[i],'\n');
return 0;
}
}
int main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
I(p),scanf("%s",S+1),n=strlen(S+1),I(Q),Init();
FOR(i,1,n)a[i]=S[i]^'0',pre[i]=add(mul(pre[i-1],10),a[i]%p);
FOR(i,1,Q)I(Que[i].l,Que[i].r),Que[i].id=i;
if(Subtask_Spe::Check())return Subtask_Spe::Cmain();
if(Subtask2::Check())return Subtask2::Cmain();
return Subtask::Cmain();
}

浙公网安备 33010602011771号