数论基础知识
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<=20001<=b<=a<=20001<=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<=1e51<=b<=a<=1e51<=b<=a<=1e5
∵\because∵ a!b!\frac{a!}{b!}b!a! (mod p)(mod\ p)(mod p) ≠≠= a! (mod p)b! (mod p)\frac{a!\ (mod\ p)}{b!\ (mod\ p)}b! (mod p)a! (mod p)
所以,可以转化为:a!∗d!a!*d!a!∗d! (mod p)(mod\ p)(mod p)
其中,did_idi 为 bib_ibi 对 ppp 的逆元,等于bip−2b_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<=1e18,1<=p<=1e51<=b<=a<=1e18,1<=p<=1e51<=b<=a<=1e18,1<=p<=1e5
11.3.1定理
设PPP为素数,a,b∈ N∗a,b\in\ N^*a,b∈ N∗,并且
a=ak∗pk+ak−1∗pk−1+⋅⋅⋅+a1∗p+a0b=bk∗pk+bk−1∗pk−1+⋅⋅⋅+b1∗p+b0a=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_0a=ak∗pk+ak−1∗pk−1+⋅⋅⋅+a1∗p+a0b=bk∗pk+bk−1∗pk−1+⋅⋅⋅+b1∗p+b0
这里0<=ai,bi<=p−10<=a_i,b_i<=p-10<=ai,bi<=p−1都是整数,i=0,1,2,⋅⋅⋅,ki=0,1,2,···,ki=0,1,2,⋅⋅⋅,k,则有
Cab≡Cakbk∗Cak−1bk−1∗⋅⋅⋅∗Ca0b0 (mod 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=Cp0∗1+Cp1∗x+Cp2∗x2+⋅⋅⋅+Cpp∗xp(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
因为ppp是素数
所以Cpi∗xiC_p^i*x_iCpi∗xi没有<p<p<p的质因子,可知Cpi∗xi≡0 (mod p)C_p^i*x_i\equiv0\ (mod\ p)Cpi∗xi≡0 (mod p)
得到(1+x)p≡1+xp (mod p)(1+x)^p≡1+x^p\ (mod\ p)(1+x)p≡1+xp (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)(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)
左右式中,xbx^bxb的系数相等可得
Cab≡Cakbk∗Cak−1bk−1∗⋅⋅⋅∗Ca0b0 (mod 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内容
Cab=a!b!∗(a−b)!C_a^b=\frac{a!}{b!*(a-b)!}Cab=b!∗(a−b)!a!
分解质因数得
a!=p1α1∗p2α2∗⋅⋅⋅∗pkαka!=p_1^{\alpha_1}*p_2^{\alpha_2}*···*p_k^{\alpha_k}a!=p1α1∗p2α2∗⋅⋅⋅∗pkαk
b!=p1β1∗p2β2∗⋅⋅⋅∗pkβkb!=p_1^{\beta_1}*p_2^{\beta_2}*···*p_k^{\beta_k}b!=p1β1∗p2β2∗⋅⋅⋅∗pkβk
(a−b)!=p1γ1∗p2γ2∗⋅⋅⋅∗pkγk(a-b)!=p_1^{\gamma_1}*p_2^{\gamma_2}*···*p_k^{\gamma_k}(a−b)!=p1γ1∗p2γ2∗⋅⋅⋅∗pkγk
其中,
α=⌊ap⌋+⌊ap2⌋+⌊ap3⌋+⋅⋅⋅\alpha=\lfloor \frac{a}{p} \rfloor+\lfloor \frac{a}{p^2} \rfloor+\lfloor \frac{a}{p^3} \rfloor+···α=⌊pa⌋+⌊p2a⌋+⌊p3a⌋+⋅⋅⋅
ppp的倍数的个数+p2p^2p2的倍数的个数+···
那么,对于CabC_a^bCab的某个质因数pip_ipi,指数为α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几何意义
给定nnn个000和nnn个111,它们将按照某种顺序排成长度为2n2n2n的序列,求它们能排成的所有序列中,能够满足任意前缀序列中0的个数不少于1的个数的序列有多少。
转化到二维坐标中,就是求从原点(0,0)(0,0)(0,0)出发到(n,n)(n,n)(n,n)满足已走路径始终是右移步数多于上移步数的方案数。即路线不超过直线y=xy=xy=x上部。
11.5.2推导
总方案数:从原点(0,0)(0,0)(0,0)出发到(n,n)(n,n)(n,n),需要右移nnn步,上移nnn步才能到达终点,方案数为C2nnC_{2n}^nC2nn步。
触碰红线:即从原点(0,0)(0,0)(0,0)出发到(n−1,n+1)(n-1,n+1)(n−1,n+1),方案数Cn−12nC_{n-1}^{2n}Cn−12n
那么合法方案数:C2nn−Cn−12nC_{2n}^n-C_{n-1}^{2n}C2nn−Cn−12n
化简后,Catalann=1n+1∗C2nnCatalan_n=\frac{1}{n+1}*C_{2n}^nCatalann=n+11∗C2nn

12. 容斥原理
12.1基本思想
先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
nnn个元素,奇数个加,偶数个减,一共2n−12^n-12n−1项
∣S1∪ S2∪ S3∣=∣S1∣+∣S2∣+∣S3∣−∣S1∩ S2∣−∣S1∩ S3∣−∣S2∩ S3∣+∣S1∩ S2∩ S3∣|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∣
Cn1−Cn2+Cn3−⋅⋅⋅+(−1)k−1Ckk=1C_n^1-C_n^2+C_n^3-···+(-1)^{k-1}C_k^k=1Cn1−Cn2+Cn3−⋅⋅⋅+(−1)k−1Ckk=1
13. 博弈论
先手必胜状态:可以走到某一个必败状态
先手必败状态:走不到任何一个必败状态
13.1Nim游戏
13.1.1 规则
有n堆石子,两个玩家轮流拿石子,每次从任意一堆中拿走任意数量的石子,不能不拿,拿到最后一个石子的玩家获胜。
13.1.2 结论
a1∧a2∧⋅⋅⋅∧an=0a_1\wedge a_2\wedge···\wedge a_n=0a1∧a2∧⋅⋅⋅∧an=0 先手必败
a1∧a2∧⋅⋅⋅∧an≠0a_1\wedge a_2\wedge···\wedge a_n \neq0a1∧a2∧⋅⋅⋅∧an=0 先手必胜
798

被折叠的 条评论
为什么被折叠?



