数论基础知识
10. 高斯消元
10.1 步骤
枚举每一列c
1.找到该列绝对值最大的一行
2.换到最上面
3.将该行第一个数变成1
4.将下面所有行的第c列消成0
const int N=110;
const double eps=1e-6;
int n;
double a[N][N];
int gauss()
{
int c,r;
for(c=0,r=0;c<n;c++)
{
int t=r;
for(int i=r;i<n;i++)
if(fabs(a[i][c])>fabs(a[t][c]))
t=i;
if(fabs(a[t][c])<eps) continue;
for(int i=c;i<=n;i++) swap(a[t][i],a[r][i]);
for(int i=n;i>=c;i--) a[r][i]/=a[r][c];
for(int i=r+1;i<n;i++)
if(fabs(a[i][c])>eps)
for(int j=n;j>=c;j--)
a[i][j]-=a[r][j]*a[i][c];
r++;
}
if(r<n)
{
for(int i=r;i<n;i++)
if(fabs(a[i][n])>eps)
return 2;//无解
return 1;//无穷组解
}
for(int i=n-1;i>=0;i--)
for(int j=i+1;j<n;j++)
a[i][n]-=a[i][j]*a[j][n];
return 0;//一组解
}
11. 组合数
11.1 递归法
1 < = b < = a < = 2000 1<=b<=a<=2000 1<=b<=a<=2000
for(int i=0;i<N;i++)
for(int j=0;j<=i;j++)
if(!j) c[i][j]=1;
else c[i][j]=c[i-1][j-1]+c[i-1][j];
11.2 通过预处理逆元的方式
1
<
=
b
<
=
a
<
=
1
e
5
1<=b<=a<=1e5
1<=b<=a<=1e5
∵
\because
∵
a
!
b
!
\frac{a!}{b!}
b!a!
(
m
o
d
p
)
(mod\ p)
(mod p)
≠
≠
=
a
!
(
m
o
d
p
)
b
!
(
m
o
d
p
)
\frac{a!\ (mod\ p)}{b!\ (mod\ p)}
b! (mod p)a! (mod p)
所以,可以转化为:
a
!
∗
d
!
a!*d!
a!∗d!
(
m
o
d
p
)
(mod\ p)
(mod p)
其中,
d
i
d_i
di 为
b
i
b_i
bi 对
p
p
p 的逆元,等于
b
i
p
−
2
b_i^{p-2}
bip−2
int fact[N],infact[N];
int qmi(int a,int k,int p)
{
int res=1;
while(k)
{
if(k&1) res=(ll)res*a%p;
a=(ll)a*a%p;
k>>=1;
}
return res;
}
fact[0]=infact[0]=1;
for(int i=1;i<N;i++)
{
fact[i]=(ll)fact[i-1]*i%mod;
infact[i]=(ll)infact[i-1]*qmi(i,mod-2,mod)%mod;//逆元
}
11.3 卢卡斯定理
1 < = b < = a < = 1 e 18 , 1 < = p < = 1 e 5 1<=b<=a<=1e18,1<=p<=1e5 1<=b<=a<=1e18,1<=p<=1e5
11.3.1定理
设
P
P
P为素数,
a
,
b
∈
N
∗
a,b\in\ N^*
a,b∈ N∗,并且
a
=
a
k
∗
p
k
+
a
k
−
1
∗
p
k
−
1
+
⋅
⋅
⋅
+
a
1
∗
p
+
a
0
b
=
b
k
∗
p
k
+
b
k
−
1
∗
p
k
−
1
+
⋅
⋅
⋅
+
b
1
∗
p
+
b
0
a=a_k*p^k+a_{k-1}*p^{k-1}+···+a_1*p+a_0\\b=b_k*p^k+b_{k-1}*p^{k-1}+···+b_1*p+b_0
a=ak∗pk+ak−1∗pk−1+⋅⋅⋅+a1∗p+a0b=bk∗pk+bk−1∗pk−1+⋅⋅⋅+b1∗p+b0
这里
0
<
=
a
i
,
b
i
<
=
p
−
1
0<=a_i,b_i<=p-1
0<=ai,bi<=p−1都是整数,
i
=
0
,
1
,
2
,
⋅
⋅
⋅
,
k
i=0,1,2,···,k
i=0,1,2,⋅⋅⋅,k,则有
C
a
b
≡
C
a
k
b
k
∗
C
a
k
−
1
b
k
−
1
∗
⋅
⋅
⋅
∗
C
a
0
b
0
(
m
o
d
P
)
C_a^b≡C_{a_k}^{b_k}*C_{a_{k-1}}^{b_{k-1} }*···*C_{a_0}^{b_0}\ (mod\ P)
Cab≡Cakbk∗Cak−1bk−1∗⋅⋅⋅∗Ca0b0 (mod P)
11.3.2证明
(
1
+
x
)
p
=
C
p
0
∗
1
+
C
p
1
∗
x
+
C
p
2
∗
x
2
+
⋅
⋅
⋅
+
C
p
p
∗
x
p
(1+x)^p=C_p^0*1+C_p^1*x+C_p^2*x^2+···+C_p^p*x^p
(1+x)p=Cp0∗1+Cp1∗x+Cp2∗x2+⋅⋅⋅+Cpp∗xp
因为
p
p
p是素数
所以
C
p
i
∗
x
i
C_p^i*x_i
Cpi∗xi没有
<
p
<p
<p的质因子,可知
C
p
i
∗
x
i
≡
0
(
m
o
d
p
)
C_p^i*x_i\equiv0\ (mod\ p)
Cpi∗xi≡0 (mod p)
得到
(
1
+
x
)
p
≡
1
+
x
p
(
m
o
d
p
)
(1+x)^p≡1+x^p\ (mod\ p)
(1+x)p≡1+xp (mod p)
利用上述结果,可知
(
1
+
x
)
a
=
(
1
+
x
)
a
0
∗
(
(
1
+
x
)
p
)
a
1
∗
⋅
⋅
⋅
∗
(
(
1
+
x
)
p
k
)
a
k
≡
(
1
+
x
)
a
0
∗
(
(
1
+
x
p
)
a
1
∗
⋅
⋅
⋅
∗
(
(
1
+
x
p
k
)
a
k
(
m
o
d
p
)
(1+x)^a=(1+x)^{a_0}*((1+x)^p)^{a_1}*···*((1+x)^{p_k})^{a_k}\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \equiv(1+x)^{a_0}*((1+x^p)^{a_1}*···*((1+x^{p_k})^{a_k}\ (mod\ p)
(1+x)a=(1+x)a0∗((1+x)p)a1∗⋅⋅⋅∗((1+x)pk)ak ≡(1+x)a0∗((1+xp)a1∗⋅⋅⋅∗((1+xpk)ak (mod p)
左右式中,
x
b
x^b
xb的系数相等可得
C
a
b
≡
C
a
k
b
k
∗
C
a
k
−
1
b
k
−
1
∗
⋅
⋅
⋅
∗
C
a
0
b
0
(
m
o
d
p
)
C_a^b\equiv C_{a_{k}}^{b_{k}}*C_{a_{k-1}}^{b_{k-1}}*···*C_{a_{0}}^{b_{0}}\ (mod\ p)
Cab≡Cakbk∗Cak−1bk−1∗⋅⋅⋅∗Ca0b0 (mod p)
11.3.3代码
typedef long long ll;
int a,b,p;
int qmi(int a,int k)
{
int res=1;
while(k)
{
if(k&1) res=(ll)res*a%p;
a=(ll)a*a%p;
k>>=1;
}
return res;
}
int C(int a,int b)
{
int res=1;
for(int i=1,j=a;i<=b;i++,j--)
{
res=(ll)res*j%p;
res=(ll)res*qmi(i,p-2)%p;
}
return res;
}
int lucas(ll a,ll b)
{
if(a<p&&b<p) return C(a,b);
return (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
}
11.4 分解质因数法求组合数
11.4.1内容
C
a
b
=
a
!
b
!
∗
(
a
−
b
)
!
C_a^b=\frac{a!}{b!*(a-b)!}
Cab=b!∗(a−b)!a!
分解质因数得
a
!
=
p
1
α
1
∗
p
2
α
2
∗
⋅
⋅
⋅
∗
p
k
α
k
a!=p_1^{\alpha_1}*p_2^{\alpha_2}*···*p_k^{\alpha_k}
a!=p1α1∗p2α2∗⋅⋅⋅∗pkαk
b
!
=
p
1
β
1
∗
p
2
β
2
∗
⋅
⋅
⋅
∗
p
k
β
k
b!=p_1^{\beta_1}*p_2^{\beta_2}*···*p_k^{\beta_k}
b!=p1β1∗p2β2∗⋅⋅⋅∗pkβk
(
a
−
b
)
!
=
p
1
γ
1
∗
p
2
γ
2
∗
⋅
⋅
⋅
∗
p
k
γ
k
(a-b)!=p_1^{\gamma_1}*p_2^{\gamma_2}*···*p_k^{\gamma_k}
(a−b)!=p1γ1∗p2γ2∗⋅⋅⋅∗pkγk
其中,
α
=
⌊
a
p
⌋
+
⌊
a
p
2
⌋
+
⌊
a
p
3
⌋
+
⋅
⋅
⋅
\alpha=\lfloor \frac{a}{p} \rfloor+\lfloor \frac{a}{p^2} \rfloor+\lfloor \frac{a}{p^3} \rfloor+···
α=⌊pa⌋+⌊p2a⌋+⌊p3a⌋+⋅⋅⋅
p
p
p的倍数的个数+
p
2
p^2
p2的倍数的个数+···
那么,对于
C
a
b
C_a^b
Cab的某个质因数
p
i
p_i
pi,指数为
α
i
−
β
i
−
γ
i
\alpha_i-\beta_i-\gamma_i
αi−βi−γi
11.4.2 代码
const int N=5010;
int sum[N],primes[N],cnt;
bool st[N];
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
int get(int n,int p)//获取指数
{
int res=0;
while(n)
{
res+=n/p;
n/=p;
}
return res;
}
vector<int> mul(vector<int> a,int b)//高精度乘法
{
vector<int> c;
int t=0;
for(int i=0;i<(int)a.size();i++)
{
t+=a[i]*b;
c.push_back(t%10);
t/=10;
}
while(t)
{
c.push_back(t%10);
t/=10;
}
return c;
}
int main()
{
int a,b;
cin>>a>>b;
get_primes(a);
for(int i=0;i<cnt;i++)
{
int p=primes[i];
sum[i]=get(a,p)-get(b,p)-get(a-b,p);
}
vector<int> res;
res.push_back(1);
for(int i=0;i<cnt;i++)
for(int j=0;j<sum[i];j++)
res=mul(res,primes[i]);
for(int i=res.size()-1;i>=0;i--)
printf("%d",res[i]);
}
11.5 卡特兰数
卡特兰数是组合数学中一个常出现于各种计数问题中的数列。其前几项为(从第0项开始):1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, …
11.5.1几何意义
给定
n
n
n个
0
0
0和
n
n
n个
1
1
1,它们将按照某种顺序排成长度为
2
n
2n
2n的序列,求它们能排成的所有序列中,能够满足任意前缀序列中0的个数不少于1的个数的序列有多少。
转化到二维坐标中,就是求从原点
(
0
,
0
)
(0,0)
(0,0)出发到
(
n
,
n
)
(n,n)
(n,n)满足已走路径始终是右移步数多于上移步数的方案数。即路线不超过直线
y
=
x
y=x
y=x上部。
11.5.2推导
总方案数:从原点
(
0
,
0
)
(0,0)
(0,0)出发到
(
n
,
n
)
(n,n)
(n,n),需要右移
n
n
n步,上移
n
n
n步才能到达终点,方案数为
C
2
n
n
C_{2n}^n
C2nn步。
触碰红线:即从原点
(
0
,
0
)
(0,0)
(0,0)出发到
(
n
−
1
,
n
+
1
)
(n-1,n+1)
(n−1,n+1),方案数
C
n
−
1
2
n
C_{n-1}^{2n}
Cn−12n
那么合法方案数:
C
2
n
n
−
C
n
−
1
2
n
C_{2n}^n-C_{n-1}^{2n}
C2nn−Cn−12n
化简后,
C
a
t
a
l
a
n
n
=
1
n
+
1
∗
C
2
n
n
Catalan_n=\frac{1}{n+1}*C_{2n}^n
Catalann=n+11∗C2nn
12. 容斥原理
12.1基本思想
先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
n
n
n个元素,奇数个加,偶数个减,一共
2
n
−
1
2^n-1
2n−1项
∣
S
1
∪
S
2
∪
S
3
∣
=
∣
S
1
∣
+
∣
S
2
∣
+
∣
S
3
∣
−
∣
S
1
∩
S
2
∣
−
∣
S
1
∩
S
3
∣
−
∣
S
2
∩
S
3
∣
+
∣
S
1
∩
S
2
∩
S
3
∣
|S_1\cup\ S_2\cup\ S_3|=|S_1|+|S_2|+|S_3|-|S_1\cap\ S_2|-|S_1\cap\ S_3|-|S_2\cap\ S_3|+|S_1\cap\ S_2\cap\ S_3|
∣S1∪ S2∪ S3∣=∣S1∣+∣S2∣+∣S3∣−∣S1∩ S2∣−∣S1∩ S3∣−∣S2∩ S3∣+∣S1∩ S2∩ S3∣
C
n
1
−
C
n
2
+
C
n
3
−
⋅
⋅
⋅
+
(
−
1
)
k
−
1
C
k
k
=
1
C_n^1-C_n^2+C_n^3-···+(-1)^{k-1}C_k^k=1
Cn1−Cn2+Cn3−⋅⋅⋅+(−1)k−1Ckk=1
13. 博弈论
先手必胜状态:可以走到某一个必败状态
先手必败状态:走不到任何一个必败状态
13.1Nim游戏
13.1.1 规则
有n堆石子,两个玩家轮流拿石子,每次从任意一堆中拿走任意数量的石子,不能不拿,拿到最后一个石子的玩家获胜。
13.1.2 结论
a
1
∧
a
2
∧
⋅
⋅
⋅
∧
a
n
=
0
a_1\wedge a_2\wedge···\wedge a_n=0
a1∧a2∧⋅⋅⋅∧an=0 先手必败
a
1
∧
a
2
∧
⋅
⋅
⋅
∧
a
n
≠
0
a_1\wedge a_2\wedge···\wedge a_n \neq0
a1∧a2∧⋅⋅⋅∧an=0 先手必胜