数论基础部分

改名!感觉还是把数学都写到这里吧。

数学菜批是这样的。

数论分块

算是前置芝士了,很多莫反题都要用这玩意。

假如现在我们有 ni 这样的式子,能够发现,其实他会有一大段值的取值是一样的,比如令 n=30,那么从 8~10 的取值都是相同的,当 n 很大的时候,这个块的长度也会很大,某些题中,我们只需要用到他的取值,那么我们遍利所有的 i 显然是冗余的,只要找到每一个块的右端点挨个看就好了,现在考虑如何找这个右端点。

甩个结论 nni 就是右端点,考虑证明。

首先令 k=ni,那么显然 kni,那么就会有 nknni=i=i,感性理解,当 i 取到最靠右的值的时候,就是 nk,也就证明了上面的结论,所以数论分块的代码写出来也是非常简洁的。

例题 P3579 [POI2014] PAN-Solar Panels

好像是因为这个题才来写的这个笔记

不是很板,带一点思考量,我们首先能够发现,假如有一个 gcd 能满足答案,感性来看就是他一定会被同时包含在 [a,b][c,d] 中间,那么如果要判断这个答案,就是令 ai<bi 时才能满足条件,[c,d] 同理。那么现在就是要考虑怎么判断令 i 同时满足两个不等式了,这就可以考虑上面的整除分块了,我们只需要枚举商,判断是否被区间包含即可,复杂度 O(nV)

code

莫比乌斯反演

我总算是懂了,现在给一个莫比乌斯函数。

μ(x)={1n=10n(1)kk

基于没有平方因子的性质,我们可以用欧拉筛求这东西,具体就是:

inline void GP(ll n){
	vis[1]=1;mu[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]) prime[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) break;
			mu[i*prime[j]]=-mu[i]; 
		}
	}
	fo(2,i,n) mu[i]+=mu[i-1];
}

莫比乌斯函数还有个性质:

d|nμ(d)={1n=10n1

也就是他等价于 ζ(n)μ1=ζ,其中 ζ 是黎曼函数, 是狄利克雷卷积,所以在狄利克雷生成函数里面,莫比乌斯函数等价于常值函数 1 的逆元。

啊啊啊,好多笔误。

考虑证明:

n=i=1kpicin=i=1kpi

显然,由二项式定理:

d|nμ(d)=d|nμ(d)=i=0k(ki)×(1)i=(1+(1))k

所以当且仅当 k=0 时原式值为 1,也就是仅当 n=1 时成立。

有个小结论,这个还是很好理解的,[gcd(i,j)=1]=d|gcd(i,j)μ(d)

现在有了上面的结论,我们开始莫反的推导,假设现在有两个在非负数域上定义的函数 f(i)F(i),并且有 F(n)=d|nf(d),那么我们存在反演结论 f(n)=d|nμ(d)F(nd)

考虑证明,首先有:

d|nμ(d)F(nd)=i|nμ(i)d|nif(d)

这一步只是将式子变了一下,然后我们更换顺序:

i|nμ(i)d|nif(d)=i|nf(i)d|niμ(d)

然后由莫比乌斯函数的性质可以得知,当且仅当 ni=1 的时候,后半部分的式子才不为 0,此时才代回前面式子,可以得到:

i|nf(i)d|nμ(d)=f(n)

得证。

其实莫反还有一种形式结论 f(n)=d|nμ(nd)F(d),这种也是好证明的。

到这里就差不多了,还有一些例题,之后会慢慢补上的。

P3455 [POI2007] ZAP-Queries

写出原答案式子:

i=1nj=1m[gcd(i,j)=k]

k 提出来扔到前面去:

i=1nkj=1mk[gcd(i,j)=1]

直接莫反:

i=1nkj=1mkd|gcd(i,j)μ(d)

再改一下枚举顺序:

d=1ni=1nkdj=1mkdμ(d)

发现 μ(d) 可以扔到前面去,那后面一坨就没什么意义了:

d=1nμ(d)nkdmkd

筛一下 μ(d) 这样我们就有了 O(n) 的解决办法,可是是多组,这样显然过不去。

进一步考虑数论分块,现将 nm 除以 k 之后就是数论分块的一般形式了,直接套板子求个和就好了,复杂度 O(nn)

code

P2398 GCD SUM

双倍经验,还是上面那个式子,枚举一下 gcd(i,j) 的值就好了。

P1829 [国家集训队] Crash的数字表格 / JZPTAB

晚自习 40min 搞出来了,但是式子长的要死。

写出原式子 i=1nj=1mlcm(i,j),把 lcm 拆开,写成 i=1nj=1mijgcd(i,j)

感觉这个分数形式很难搞,我们仍然采用枚举 gcd 的想法搞,这里跳了一步,直接把 [gcd(i,j)=k] 弄到外面去,然后会留下 k2 的常数:

k=1n1ki=1nkj=1mkk2ij[gcd(i,j)=1]

k2 放到外面,然后常规莫反套路,改一下 gcd 式子再莫反:

k=1nkd=1nki=1nkdj=1mkdd2ij μ(d)

之后把无关的 d2μ(d) 扔出去,发现 i 与后面的一个求和无关,放到前面:

k=1nkd=1nkd2μ(d)i=1nkdij=1mkdj

这样的话,我们可以处理 Ai=j=1ij 就可以把式子表示成:

k=1nkd=1nkd2μ(d)AnkdAmkd

把后面记作 f(n)=d=1nd2μ(d)AndAmd,这东西只要预处理 μ(d)d2 就可以数论分块了,复杂度 O(n)

可是还有前面枚举 kO(n),这样总的是 O(nn),无法通过。

写出式子 k=1nk f(nk),发现这东西可以再套一个数论分块,这样总的复杂度就是 O(n) 了,可以通过。

code

P2257 YY的GCD

滚回来更新了,这题好像四个月前就在做题计划里了,一直没补(

好像有一个单测的简化版,简单写一下式子吧,我们默认令 n<m

kprimei=1nki=1mk[gcd(i,j)=1]

然后莫反:

kprimed=1nki=1nkdi=1mkdμ(d)

μ(d) 提到前面去,自然变成:

kprimed=1nkμ(d)nkdmkd

这样单次时间复杂度就是 O(Vn) 了,其中 V 表示 prime 的大小,可是这题多测,O(TVn) 跑不过去,考虑再优化:

T=dk,我们改为优先枚举 T,则式子变成:

T=1nnTmTk|T,kprimeμ(Tk)

这时候我们就发现后面的东西可以预处理了,在欧拉筛之后枚举一下倍数是 O(nlogn) 的。

code

P3704 [SDOI2017] 数字表格

大力膜拜 lhc!!!

写出式子,默认 n<mf(i) 表示 fib 序列:

i=1nj=1mf(gcd(i,j))

枚举 gcd,得到:

d=1nj=1ndj=1mdf(d)[gcd(i,j)=1]

发现 f(d) 进行乘法的次数就是 i=1nj=1m[gcd(i,j)=1]

d=1nf(d)i=1ndj=1md[gcd(i,j)=1]

之后正常莫反套路:

d=1nf(d)k=1ndμ(k)nkdmkd

然后我们里面的 拿出来,对里面分成两部分:

d=1nk=1nd(f(d)μ(k))nkdmkd

模仿上一题的优化套路,我们先令 T=kd,然后枚举 T,改一下式子:

T=1n(d|Tf(d)μ(Td))nTmT

我们设 F(n)=d|nf(d)μ(nd),原式变成:

T=1nF(T)nTmT

因为 n106,所以 F(T) 是可以枚举因数做到预处理 O(nlogn) 的,再做一遍数论分块就好了,复杂度大概是 O(Tnlogn)

code

扩展欧几里得算法

求解不定方程,形似 ax+by=gcd(a,b) 的解。

洛谷的模板题是求 ax+by=c,因为显然有 gcd(a,b)|(ax+by),然后就要保证 gcd(a,b)|c,把通解形式改写一下就好了。

感觉 oi-wiki 的证法比较好理解,写一下:

我们设:

存在 ax0+by0=gcd(a,b)bx1+(amodb)y1=gcd(b,amodb)

要证明这俩方程是等价的,首先有 gcd(a,b)=gcd(b,amodb),然后写成 ax0+by0=bx1+(amodb)y1

对于 amodb,等价于 a=aab×b

推一下式子,有 ax0+by0=ay1+b(x1aby1)

那么就有 x0=y1y0=x1aby1,递归求解即可。

inline ll Exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b) return x=1,y=0,a;
	ll d=Exgcd(b,a%b,x,y);
	ll t=x;x=y,y=t-(a/b)*y;return d;
}

卢卡斯定理

对于一个质数 p,我们可以用卢卡斯定理求 (nm)modp

有结论是 Lucas(n,m)=Lucas(n/p,m/p)×(nmodpmmodp),这个是直接递归下去求就好了,复杂度大概是 O(logn)

等我尽量补一下证明。

inline ll qpow(ll a,ll b){return (!b?1ll:qpow(a*a%p,b>>1)*((b&1ll)?a:1ll))%p;}
inline ll C(ll n,ll m){if(m>n) return 0;return pre[n]*qpow(pre[m],p-2)%p*qpow(pre[n-m],p-2)%p;}
inline ll Lucas(ll n,ll m){return (!m?1ll:C(n%p,m%p)*Lucas(n/p,m/p)%p);}
signed main(){
	ll t;read(t);
	while(t--)
	{
		read(n),read(m),read(p);
		pre[0]=1;fo(1,i,max(n+m,p)) pre[i]=pre[i-1]*i%p;
		wr(Lucas(n+m,n)),pr; 
	}
}

扩展欧拉定理

哦这个好久之前就写过博客了,写的好丑啊我靠。

欧拉函数

这里给出定义 :

我们记 φ(n) 为小于等于nn互质的数的个数。

展开是这样的:

φ(x)=i=1x[ gcd (i,n)=1]

欧拉筛显然可以把它筛出来:

inline void check_mul(ll n){
	vis[1]=1,mul[1]=1;
	fo(2,i,n)
	{
		if(!vis[i]) prime[++cnt]=i,f[i]=i,mul[i]=-1;
		for(ll j=0;j<cnt&&i*prime[j]<=n;j++){
			vis[i*prime[j]] = 1;
			f[i*prime[j]] = f[i] + 1;
			if(i%prime[j]==0){
				mul[i*prime[j]]=0;
				break;
			}
			mul[i*prime[j]]=-mul[i];
		}
	}
}

对于单个欧拉函数的求法:

欧拉函数是特殊的积性函数,有性质 φ(ab)=φ(a)φ(b),对于任意一个正整数 a,有 a=i=1npiki

进一步,我们可以发现对于任意一个质数 p,有 φ(pk)=pk1(p1)

因此,有 φ(a)=i=1nφ(piki)=i=1npiki1(pi1)=i=1npikii=1n(11pi)=ai=1n(11pi)

这样我们就可以在 O(a) 的复杂度内处理出 φ(a)

欧拉定理

直接给出结论:

gcd(a,m)=1 , 则有 aφ(m)1 (mod m)

证明比较简单:

我们假定 r1,r2,r3...rφ(m) 为模 m 下的一个剩余系,则 ar1,ar2...arφ(m) 也为模 m 下的一个剩余系,所以有 r1r2r3...rφ(m)ar1ar2ar3...arφ(m) 同时约去剩余系 , 即可得到上述公式 aφ(m)1 (mod m)

具体证明可以参考 oiwiki

拓展欧拉定理

对于欧拉定理,只有在 gcd(a,m)=1 时才能使用,那么我们推广一下便可得到:

ab{abmodφ(p),gcd(a,p)=1,ab,gcd(a,p)1,b<φ(p),(modp)abmodφ(p)+φ(p),gcd(a,p)1,bφ(p)

证明过程类似跳循环节,此处不再写。

那么,我们只需要预处理出 φ(m) 就可以按照拓展欧拉定理进行降幂了。

inline bool read(ll &opp,ll mod){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}ll flag=0;while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);if(x>=mod) flag=1,x%=mod;ch=getchar();}opp=x*t;return flag;}
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
inline ll qpow(ll a,ll b,ll mod){return (!b?1ll:qpow(a*a%mod,b>>1,mod)*((b&1)?a:1ll))%mod;}
ll a,m,b;
signed main(){
	read(a),read(m);
	ll k=m,phi=m;
	fo(2,i,sqrt(m))
	  if(k%i==0)
	  {
		phi=phi/i*(i-1);
		while(k%i==0) k/=i;
	  }
	if(k>1) phi=phi/k*(k-1);
	ll op=read(b,phi);if(op) b+=phi;
	wr(qpow(a,b,m)),pr;
}

中国剩余定理(CRT)

考虑我们现在有同余方程组:

{xa1(modb1)xa2(modb2)...xan(modbn)

对于这个方程组的求解,我们有结论:

n=bimi=nbici=mi(mi1modbi)

则有 ans=aici

考虑证明,我们要证明 ansai(modbi)

对于任意 ij,显然有 mjcj0(modbi),而又有 cimi(mi1modbi)1(modbi)

于是就可以推到出,对于每一个 i,有:

aiciaiciaimi(mi1modbi)ai(modbi)

得证,CRT 的正确性就是需要 bi 互质,不互质的话就需要 exCRT 了,求 m1 时需要用到 exgcd。

inline ll Exgcd(ll a,ll b,ll &x,ll &y)
{
	if(!b) return x=1,y=0,a;
	ll d=Exgcd(b,a%b,x,y);
	ll t=x;x=y,y=t-(a/b)*y;return d;
}
signed main(){
	read(n);fo(1,i,n) read(a[i]),read(b[i]),sum*=a[i];
	fo(1,i,n)
	{
		ll opt=sum/a[i],x,y;
		Exgcd(opt,a[i],x,y);
		ans=(ans+b[i]*opt*x%sum)%sum;
	}
	wr((ans%sum+sum)%sum),pr;
}
posted @   Wei_Han  阅读(27)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示