[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();
} 
posted @ 2025-01-22 11:12  Add_Catalyst  阅读(11)  评论(0)    收藏  举报