数学

预计是遇到各种零散的东西就把它们记下来,记得足够多的时候整理一下。

参考自各种奇奇怪怪的博客知乎 B 站和机房大佬。

因为博主显然对这些东西的理解也并不算深刻,看着玩吧。有机会就优化一下。

参考资料:


数论类

费马小定理

  • aP11(modP)P 为质数。

待填。

应用

求逆元

a1,在模意义下做除法时有用。同时乘 a1aP2a1(modP)。所以模数是质数的时候快速幂一下就可以逆元了。

裴蜀定理

  • 二元一次不定方程 ax+by=c(a,x,b,y,cZ) 有整数解仅当 cgcd(a,b)

扩展欧几里得算法(exgcd)

可以在用欧几里得算法求 gcd 的时候顺便解出 ax+by=gcd(a,b) 的一组特解。

流程

其实挺妙的。本质上是通过反复递归构造同样形式的问题直到变成一个相当简单的方程,直接求解后一层层带回去。

由欧几里得算法得 gcd(a,b)=gcd(b,amodb)。考虑不断令 ab,bamodb,这样到最后一定会有 b=0,直接可以确定特解 x=1,y=0

回带的时候考虑 x,y 怎么变化。假设有 ax+by=bx+(amodb)y,且我们已经回带得到了 x,y 的一组特解。那么有 ax+by=bx+(amodb)y=ay+b(xaby)。在递归下去的时候把 x,y 这么变一下就好了。

博主为了压代码长度放弃了求 gcd 的功能。

void ecd(ll a,ll b,ll &x,ll &y) { b?(ecd(b,a%b,y,x),y-=a/b*x):(x=1,y=0); }

这样求完之后 x,y 就是特解了。

解的范围

|x|12|a|,|y|12|b|

证明相当复杂

总之以后用 exgcd 的时候大概率就不需要担心爆 int/long long 的问题了。

应用

求逆元

有费马了还要 exgcd 干嘛啊。

模数不是质数的时候你就寄喽。

若我们要求 n 在模 P 意义下的逆元而且 n,P 互质,那么可以令 ax+by=gcd(a,b) 中的 a=n,b=P,此时 gcd(a,b)=1,解出来的 x 就是逆元了,y 是转化取模的。

很显然在 n,P 不互质的时候解出来的就不知道是什么东西了……

求解二元一次不定方程

你需要求 ax+by=c 的一组整数解。根据前面的裴蜀定理,有解仅当 gcd(a,b)|c

所以我们把 a,b 同时除以 c 之后用 exgcd 求一组特解,然后再把 c 乘回去就行了(也就是说解的范围也乘了 c)。

不过通常问题不会这么简单,题目大概率对求出的解有限制,比如要求 x 是最小正整数时的解之类的,或者有时候会让你算有多少组正整数解。所以我们需要知道 x,y 的通解形式。

p=bgcd(a,b),q=agcd(a,b)(正负性反过来也行),如果 x0,y0 是一组特解,那么 {x=x0+pky=y0+qk 是方程的通解。感觉挺显然的。

然后针对题目限制去同时变化 x,y 就好了。比如最小正整数解就算一下这时 k 是多少,正整数解的个数就算 x 是最小正整数和 y 是最小正整数时的 k 差了多少。

线性同余方程组问题

{xa1(modm1)xa2(modm2)xan(modmn),求(通常是最小的正整数/最小的大于某个值的/最大的小于某个值的)x

中国剩余定理(CRT)

mi 全部互质时可以用。

考虑对于每个方程构造一个 xi 使得 xi 恰好只满足这个方程,而且在其它方程的模意义下都为 0,这样把所有的 xi 加起来得到的 x 就能满足所有的方程了。所以问题就在于怎么构造 xi。令 M=mi

首先如果要在其他方程的模意义下为 0,利用互质的性质,设 ci=jimi=Mmi 就解决了。

然后看怎么让 xi 还满足它对应的方程。令 dici1(modmi),那么 xi=aicidi 即满足以上条件。这个就比较神奇了……在这个方程的模意义下,这么做相当于 cidi 乘起来为 1,抵消了,只剩一个 ai 满足条件。但是在其它方程的模意义下,同样由于互质,只要模数不同,ci,di 就抵消不掉,ci 依然作为 mj 的倍数,所以模出来还是 0

只要没有模数同时对应两个不同余数就有解。

ll crt(ll n,ll *m,ll *a) {
	ll mul=1; __int128 res=0;
	for (ll i=1;i<=n;i++) mul*=m[i];
	for (ll i=1,x;i<=n;i++) ecd(mul/m[i],m[i],x,*new ll),res+=(__int128)a[i]*(mul/m[i])*(x+mul);
	return res%mul;
}

扩展中国剩余定理(exCRT)

这个不需要 mi 互质,而且除了名字和求解的问题之外和 crt 没有任何关系。本质上是不断合并同余方程直到剩一个。

假设有两个方程 xa1(modm1),xa2(modm2),我们要把它们合并成一个方程。

x=pm1+a1=qm2+a2,得 pm1qm2=a2a1,这是一个二元一次不定方程,直接 exgcd 解出来就好了。然后合并完的新方程是 xpm1+a1(modlcm(m1,m2))。合到只剩一个方程即可,对着题目要求求一个合法的 x 就结束了。

复杂度也是 O(nlog)

ll excrt(ll n,ll *m,ll *a) {
	for (ll i=2;i<=n;i++) {
		ll g=__gcd(m[i],m[1]),f=a[i]-a[1],p,q;
		if (f%g!=0) return -1;
		m[i]/=g,ecd(m[1]/g,m[i],p,q);
		a[1]+=(__int128)f/g*p%m[i]*m[1],m[1]*=m[i],a[1]%=m[1];
	}
	return (a[1]%m[1]+m[1])%m[1];
}

数论分块

求解类似 i=1nf(ni) 之类问题时可以用。原理是 ni 的取值只有 O(n) 种,就把每种 f(ni) 乘上它出现的次数。

如果是 f(ni,mi,) 这样多个参数的话,只要里面都是整除的形式也能用,可以当作是几个参数就是几倍常数。(?)

以两个参数 n,m 的为例,一种非常简单的写法如下,[l,r] 就是同样的 f 出现的区间。

for (int l=1,r;l<=min(n,m);l=r+1) {
	r=min(n/(n/l),m/(m/l));
	sum+=(r-l+1)*f(n/l);
}

欧拉函数(φ)

  • φ(n)[1,n] 中与 n 互质的数的个数。

性质

  • φ(n) 是积性函数。即当 a,b 互质时,φ(ab)=φ(a)φ(b)
  • P 为质数时,φ(Pk)=(P1)Pk1
  • 计算式:设 n 被唯一分解为 piki,则 φ(n)=npi1pi
  • 线性筛:

其实基本所有满足积性函数的数论函数都可以线性筛。φ 也同理。

struct mth {
	int vis[N],prm[N],cnn;
	int phi[N];
	mth() {
		vis[0]=vis[1]=1,phi[1]=1;
		for (ll i=2;i<N;i++) {
			if (!vis[i]) prm[++cnn]=i,phi[i]=i-1;
			for (ll j=1;j<=cnn&&i*prm[j]<N;j++) {
				vis[i*prm[j]]=1;
				if (i%prm[j]==0) { phi[i*prm[j]]=phi[i]*prm[j]; break;}
				phi[i*prm[j]]=phi[i]*phi[prm[j]];
			}
		}
	}
} V;
  • d|nφ(d)=n

欧拉定理

  • aP 互质时,aφ(P)1(modP)

P 为质数的时候和费马小定理本质相同。一个应用是可以用来确定指数取模的时候要模什么东西。

扩展欧拉定理

  • ab{abb<φ(n)a(bmodφ(n))+φ(n)bφ(n)(modn)

用于解决 a,n 不一定互质的问题。

应用:欧拉降幂

众所周知指数不能直接取模。那么用扩展欧拉定理就能知道指数要模什么了。前提是你知道指数和 φ 的大小关系。

莫比乌斯函数(μ)

n 被唯一分解为 n=i=1kpici,定义 μ(n)={1,n=1,0, ci2,(1)k,otherwise.

莫比乌斯反演

若有函数 f(n)=d|ng(d),那么可以反过来得到 g(n)=d|nμ(d)f(nd)

还有一种也比较常用(?)的形式 f(d)=d|ng(n)g(d)=d|nμ(n)f(nd)

性质

  • i|nμ(i)=[n=1]

可用于将某些 [...=c] 之类很烦的限制转为和式然后一顿乱搞,比如 gcd=1

  • d|nμ(d)d=φ(n)n

由之前结论 d|nφ(d)=n,反演得 φ(n)=d|nμ(d)nd,整理一下。

离散对数问题

anb(modP),已知 a,b,P,求(最小的)n。即在模 P 意义下求 logab

可以发现这玩意你不太好直接算出来。

大步小步算法(BSGS)

a,P 互质时可以使用北上广深 BSGS。

一个个枚举 n 太慢了。能不能预处理一部分然后枚举一部分?

考虑令 n=pBq,其中 0q<B。我们预处理出所有 aqb 的值并塞进哈希表里(小步),然后枚举 p 的取值。如果发现枚举到一个 p 时存在 aqb=apB(大步),那么就可以直接拿出那个哈希表里的值算一下得到 n。要是最后都找不到 n 就是无解。

显然 BP 时最优。于是我们获得了一个 P 的做法。

有些阴间情况要特判。

ll bgs(ll a,ll b,ll P) {
	a%=P,b%=P;
	if (a==0&&b!=0) return -J;
	if (P==1||b==1) return 0;
	ll c=sqrt(P)+1,x=1,y=1; unordered_map<ll,ll> mp;
	for (ll i=0;i<c;i++) mp[x*b%P]=i,(x*=a)%=P;
	for (ll i=1;i<=c;i++) if (mp.count((y*=x)%=P)) return i*c-mp[y];
	return -J;
}

扩展大步小步算法(exBSGS)

a,P 不互质时会有一个问题:apBaqb(modP) 推不回 apBqb(modP),因为 a 不一定有逆元。

所以这时我们考虑把 a,P 通过一些神秘技巧变成互质的。

d1=gcd(a,P),然后把整个方程除以 d1 变为 an1(ad1)1bd1(modPd1)。因为 ad1,Pd1 互质,所以 ad1 在模 Pd1 意义下存在逆元,可以 exgcd 整出来。至于 b,同样由于不一定有逆元的问题,如果 bmodd1 不为 0 就无解了(1 除外)。记得取模。

这时可能 aPd1 还是不互质,于是再令 d2=gcd(a,Pd1),方程变为 an2(ad1d2)1bd1d2(modPd1d2),若 (ad1)1bd1modd2 不为 0/1 无解……

直到最后得到 ank(ad1d2dk)1bd1d2dk(modpd1d2dk),此时 apd1d2dk 互质,可以跑 BSGS 了。跑完记得答案加 k

ll egs(ll a,ll b,ll P) {
	a%=P,b%=P;
	ll res=0;
	for (ll d;(d=__gcd(a,P))!=1;res++) {
		if (b==1) return res;
		if (b%d) return -J;
		P/=d,b=b/d*inv(a/d,P)%P,a%=P;
	}
	return bgs(a,b,P)+res;
}

质因数分解

费马素性测试

Miller-Rabin 素性测试

Pollard-Rho

组合类

定义

Cmn=(mn)={m!(mn)!n!mn0m<nm,nN

博主倾向于写 (mn)

计算

使用定义

一般没有模数要写高精而且 m,n 巨大但是 mn 较小而且查询次数较少的时候才会用。

递推式

(mn)=(m1n1)+(m1n)(杨辉三角)。

没有模数时这样可以不用写乘 & 除。

普遍求法

对大质数模数 P 取模时,可以预处理阶乘和阶乘逆元实现单次 O(1) 查询。记得特判掉 m<0,n<0,m<n

卢卡斯定理(Lucas)

然而当模数 P 为小质数(m,n)时上面的方法完蛋了,m,nP 倍数直接寄掉。这时需要卢卡斯定理:在 P 为质数时,有:

(mn)(mPnP)(mmodPnmodP)(modP)

人话就是把商和余数分离出来是没有影响的。证明等博主会了再说。

右边那项的 P 变成了大质数,预处理阶乘逆元直接算,左边的递归处理。

预处理 O(P),单次查询 O(logPn)

CRT 套卢卡斯定理

把模数分解为一堆小质数,分别用卢卡斯定理求出组合数模这些小质数时得到的答案,再 CRT 把这堆玩意合起来。复杂度是 O(log2)。这样理论上是可以实现任意模数求组合数的。

扩展卢卡斯定理(exLucas)

还有一种更为直接的任意模数求组合数:当卢卡斯定理里的 P 不是质数时,可以用扩展卢卡斯定理。

性质

  • 范德蒙德卷积:

博弈类

Nim 游戏

  • n 堆石子,两个人轮流取,每次可以取走一堆石子里的若干个。但不能不取,没得取的输,问先手后手谁有必胜策略。

直接摆结论。设 n 堆石子数分别为 ai,那么后手必胜仅当 xorai=0,否则先手必胜。

简单理解:因为异或运算的奇妙性质,只要某一刻 xorai0,此时操作的人必有一种方法使得下一步 xorai=0,而显然如果这一刻 xorai=0,操作完必定 xorai0。已知最后石子一定被取完,正好有 xorai=0,所以只要先手拿到的状态是 xorai0,他就必定有方法反复让 xor 的结果反复横跳,把后手干碎。反之他自己就会被后手干碎。这种必胜条件的推导可以参考一下。

SG 函数

  • 有一张无环的有向图,上面有若干个棋子(可以有多个棋子在同一点上),两个人轮流操作,每次可以将一个棋子往它所在的点的一条出边移动一步,动不了的输,问先手后手谁有必胜策略。

先考虑只有一个棋子的情况。

定义一个博弈状态的 SG 函数 sgi 为,在这个状态能一步到达的所有其它的状态的 sgj 中,最小的没有出现的非负整数,即 mexsgj。在只有一个棋子的此问题中,博弈状态即为这个棋子在哪个点,能到达的其它状态就是这个棋子走一步能到的点。那么如果起始点的 sgs0,先手必胜。

因为 mex 有性质:一个点 i 一步能到所有 sgj<sgisgj。那么如果一个人把一个棋子移到了 sgj>sgi 的点,那么下一个人一定能再把 sgj 移到 sgk=sgi 的点,相当于根本没操作过,那么这就直接是 Nim 游戏了。

SG 函数的逆天之处在于它能在多个棋子的时候也能用。也就是如果一个博弈问题可以拆成若干个博弈子问题(子问题全部终止时大问题才终止,每次操作只能影响一个子问题),那么大问题的 SG 就是小问题的 SG 异或和。在这个问题里先手必胜仅当每个棋子单独的 sgs 的异或和不为 0,证明待补,或许是这可以看成另一种形式的 Nim 问题。

多项式

期望概率类

概率

期望

期望可以理解为所有可能出现的情况的答案取个平均值。

形式化的就是 E(x)=xA(x)P(x)x 表示一种可能出现的情况,A(x) 是这种情况的答案,P(x) 是它出现的概率。

性质

  • 线性性:通俗来说就是和的期望等于期望的和(E(A+B)=E(A)+E(B))。这个好像有些时候不成立,但是博主暂未理解,待补。

应用:

  • 求一个骰子投两遍朝上那面的点数之和的期望。

设第一次和为 a,第二次和为 b。那么答案为 E(a+b)=E(a)+E(b),即对两次投掷分别求期望加起来。已知 E(a)=E(b)=3.5,于是 E(a+b)=7

n 个糖,每个糖有一个种类 ci。对于每一个 k[1,n] 问:你会从中随机选 k 颗糖吃掉,问吃掉的糖的不同种类数的期望。

1n5×1041ci109,4s,1GB。

根据线性性,期望可以拆成每种糖选的概率加起来(因为拆完之后 A(x) 这里等于 1 所以期望就是概率)。

设种类 i 出现的次数为 cnti,那么它给答案带来的贡献为 (nk)(ncntik)(nk),即总情况数减去没选到的情况数再除以总情况数。加起来就为答案 i((nk)(ncntik))(nk)

现在是 O(nci) 的,得用神威太湖之光才能过去。考虑两个优化:

  • 只算输入过的 ci。变 O(n2)
  • 将相同的 cnti 放在一起统一计算。因为 cnti=n,不同的 cnti 最多有 O(n) 个。变 O(nn)。可以过了。
posted @   CarroT1212  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示