数论——Knowledge Point&Problems

(文前友情提醒:此篇博客略有些长,最近上得快,没太多时间更新)

本文要点:

欧拉筛法

欧拉函数

扩展欧几里得

逆元

同余方程

BSGS

矩阵乘法

高斯消元

线性空间

组合计数

M o ¨ b i u s M\ddot{o}bius Mo¨bius函数

如果有您需要的,就请往下翻吧!


1.欧拉筛法。

暂无。

相关题目:

1.Prime Distance(check here)

题目描述:给你一个L,一个R,求L-R中最近的两个质数以及最远的两个质数。

范围: 1 ≤ L , R ≤ 2147483647 , R − L < = 1 0 6 1\le L,R \le2147483647,R-L<=10^6 1L,R2147483647RL<=106

Solution:

首先对于 L , R L,R LR,非常的大,已知的所有筛法都无法在规定时间内求解。但是可以看到范围里还有很鬼畜的一条东西: R − L < = 1 0 6 R-L<=10^6 RL<=106。根据它我们可以看出来,我们是可以扫描 L − R L-R LR的。
考虑到一个合数K至少有一个小于等于 K \sqrt{K} K 的因数,因此我们用线性筛筛出 R \sqrt{R} R 的所有质数,将 L − R L-R LR中所有该质数的倍数全部除去,最后再进行一边扫描,找题目的要求就可以了。
这里因为 L L L R R R比较大,我们可以映射到一个区域,来保证数组可以开下。

code:

#include 
using namespace std;



int read(){//快读 
   int s=0,w=0;
   char ch=getchar();
   for(;ch<'0'||ch>'9';ch=getchar())s|=ch=='-';
   for(;ch>='0'&&ch<='9';ch=getchar())w=(w<<3)+(w<<1)+(ch^48);
   return s?-w:w;
}



int L,R;
int vis[65537],pr[15001],tot,issp[1000001];
//vis数组表示一个数是否为质数
//pr数组则是记录具体的质数是哪些
//issp数组表示的是每次L和R区间哪些数是质数 



void Prime(){
    //筛法,此处使用了欧拉筛法 
	vis[1]=1;
	for(int i=2;i<=65536;++i){
		if(!vis[i]){
		    pr[++tot]=i;
		}
		for(int j=1;j<=tot;++j){
			if(i*pr[j]>65536){
				break;
			}
			vis[i*pr[j]]=1;
			if(!i%pr[j]){
				break;
			}
		}
	}
	return;
}



int shift(int l,int r,int prime){
    //该函数用来计算一个质数的多少倍是第一个出现在L-R区间中的。 
	//返回的是它与L的距离(因为L,R较大,所以我们为了避免开大数组而采用了让所有下标减去L的方法来缩小范围) 
	long long x=((long long)l+(long long)prime-(long long)1)/(long long)prime;
	long long y=x*prime-l+1;
	return int(y);
}



int main(){
	
	Prime();
	
	int TT=0,Max,Maxi,Maxj,Min,Mini,Minj;
	//设置TT是为了方便每次issp数组的使用,因为凉心出题人的存在而为了节约时间。对于每次修改issp就改为TT,查询改为是否是TT这也是竞赛中为了节约时间的常用方法。
	//Max记录两个质数距离最大是多少,Min则记录两个质数距离最小是多少。 
	//Maxi,Maxj则为这两个质数是谁,Mini,Minj同理。 
	
    while(cin>>L){
    	++TT;
    	R=read();
    	if(L==1)issp[L]=TT;//emmm特判1 
    	int lastpr=-1;
    	Max=-0x3f3f3f3f,Maxi=-1,Maxj=-1,Min=0x3f3f3f3f,Mini=-1,Minj=-1;
		//这些是初始工作 
    	
    	for(int i=1;i<=tot;++i){
    		if(pr[i]*pr[i]>R){
				break;
				//质数是否出界 
			}
    		if(pr[i]<L){
				for(int j=shift(L,R,pr[i]);j<=R-L+1;j+=pr[i])
					issp[j]=TT;
					//记录是否为质数,不是则为TT 
			}
    		else{
				for(int j=int((long long)pr[i]*2-(long long)L+1);j<=R-L+1;j+=pr[i])
				    issp[j]=TT;
				    //同理 
			}
			//此处分类讨论了,或许可以不讨论。 
		}
		//这边是将非质数筛去。 
		
		for(int j=1;j<=R-L+1;++j){
			if(issp[j]!=TT&&lastpr==-1){
				lastpr=j;
				//lastpr意为上一个质数。 
			} 
			else if(issp[j]!=TT&&lastpr!=-1){
				if(j-lastpr>Max){
					Max=j-lastpr;
					Maxi=lastpr+L-1;
					Maxj=j+L-1;
				}
				//找最大距离。 
				if(j-lastpr<Min){
					Min=j-lastpr;
					Mini=lastpr+L-1;
					Minj=j+L-1;
				}
				//找最小距离。 
				lastpr=j; 
				//修改上一个质数。 
			}
		}
		//寻找最大距离和最小距离的过程
		 
		if(Maxi!=-1&&Maxj!=-1){
			printf("%d,%d are closest, %d,%d are most distant.\n",Mini,Minj,Maxi,Maxj);
		} 
		else{ 
		    puts("There are no adjacent primes.");
		}
		//输出即可。
		 
	}
	return 0;
}

2.欧拉函数。

暂无。

相关题目:

2.GCD(check here)

题目描述:给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对。

范围: 1 ≤ n ≤ 1 0 7 1\le n\le10^7 1n107

Solution:

范围香不香?所以你根本无法枚举,当然部分分是可以有的。
考虑一下2个数的 g c d ( x , y ) gcd(x,y) gcd(x,y),这等于一个质数。
质数? 1 0 7 10^7 107?我们知道是有筛法可以完成 1 s 1s 1s肝爆 1 0 7 10^7 107的,是不是意味着我们可以将它往素数那一方面想呢?
再考虑以下:设 g c d ( x , y ) = z gcd(x,y)=z gcd(x,y)=z,那么 g c d ( x z , y z ) gcd(\frac xz,\frac yz) gcd(zx,zy)是等于多少的呢?那就是 1 1 1了。
1 1 1?那么:假设y已经固定,那么对于 1 − y 1-y 1y这个区间中,这样的 x x x有多少呢?无脑思考便可以得出结论: φ ( n ) φ(n) φ(n)个。
so?对于每个小于 n n n的质数,计算的便是 1 ≤ x , y ≤ n p 1\le x,y\le \frac np 1x,ypn中互质的 x , y x,y xy。这样的对有多少个呢?对于每个 y y y,设 x ≤ y x\le y xy,那么便有 φ ( y ) ∗ 2 − 1 φ(y)*2-1 φ(y)21个(减 1 1 1是因为 x x x),所以我们可以做一遍前缀和,方便快速地计算。
至此,本题得到了解决。

code:

#include 
using namespace std;



int read(){//快读 
   int s=0,w=0;
   char ch=getchar();
   for(;ch<'0'||ch>'9';ch=getchar())s|=ch=='-';
   for(;ch>='0'&&ch<='9';ch=getchar())w=(w<<3)+(w<<1)+(ch^48);
   return s?-w:w;
}



int vis[10000001],prime[10000001],tot,phi[10000001],n;
//vis数组是用来记录该数字是否为质数。 
//prime数组则是具体的质数是哪些。 
//phi数组是欧拉函数。 

long long s[10000001];
//这是前缀和数组。 



int main(){
	n=read();
	for(int i=1;i<=n;++i)
	    phi[i]=i;
	//初始工作。 
	 
	for(int i=2;i<=n;++i){
		
		if(!vis[i]){
			vis[i]=i;
			prime[++tot]=i;
			phi[i]=i-1;
		}
		//这个是i是质数的情况。
		 
		for(int j=1;j<=tot;++j){
			
			if(prime[j]>vis[i]||prime[j]>n/i){
				break;
			}
			
			vis[i*prime[j]]=prime[j];
			
			phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
			//根据欧拉函数的性质去快速计算。
			 
		} 
	}
	//此处是欧拉筛法求欧拉函数。
	 
	for(int i=1;i<=n;++i)
	    s[i]=s[i-1]+(long long)phi[i];
	//计算前缀和。
	 
	long long S=0;
	
	for(int i=1;i<=tot;++i)
	    S+=s[n/prime[i]]*2-1;
	//计算答案。
	 
	printf("%lld",S);
	//输出。 
    return 0;
}

3.扩展欧几里得。

暂无。

相关题目:

3.青蛙的约会 (check here)

题目描述:求 x + k m ≡ y + k n ( m o d l ) x+km≡y+kn\pmod l x+kmy+kn(modl)

范围: 0 ≤ x = ̸ y ≤ 2000000000 0\le x =\not y \le2000000000 0x≠y2000000000 1 ≤ m , n ≤ 2000000000 1\le m,n \le2000000000 1m,n2000000000 1 ≤ L ≤ 2100000000 。 1\le L \le2100000000。 1L2100000000

Solution:

先将它给的锑式转化。可以得到: x + k m ≡ y + k n ( m o d l ) x+km≡y+kn\pmod l x+kmy+kn(modl)那么这个式子必然需要整治。所以我们开始魔改
x + k m ≡ y + k n ( m o d l ) → x − y + k ( m − n ) ≡ 0 ( m o d l ) → x+km≡y+kn\pmod l →x-y+k(m-n)≡0\pmod l → x+kmy+kn(modl)xy+k(mn)0(modl)
k ( m − n ) − a ∗ l = y − x k(m-n)-a*l=y-x k(mn)al=yx
这已经很像不定方程了!
再次改进,我们设n-m为A,y-x为-B,那原式变为 k ∗ A + a ∗ l = B k*A+a*l=B kA+al=B
这就可以使用扩欧解决。
但是请考虑一个问题,扩欧的式子: a ∗ x + b ∗ y = g c d ( a , b ) a*x+b*y=gcd(a,b) ax+by=gcd(a,b),所以我们所求的只是一个特解。
那么所有解如何表示呢?
看:我们求得了一个 a ∗ x 0 + b ∗ y 0 = c a*x0+b*y0=c ax0+by0=c,用这个式子减去最初的式子可以得到: a ∗ ( x − x 0 ) + b ∗ ( y − y 0 ) = 0 a*(x-x0)+b*(y-y0)=0 a(xx0)+b(yy0)=0
k = g c d ( a , b ) k=gcd(a,b) k=gcd(a,b)两边同除 k k k,这样可以使得新的 a , b a,b ab互质,就可以得知: b k \frac bk kb| x − x 0 x-x0 xx0的(或者 a k ∣ y 0 − y \frac ak|y0-y kay0y,这里我们考虑前面的)。所以我们便可以任意的去添加k而不会使得该等式不成立。所以最小的x0就是当x0与k互质时(即x0中不含k),那么便可得知方程的最小解。但是上面的式子右边是 g c d ( a , b ) gcd(a,b) gcd(a,b),设 S = g c d ( a , b ) S=gcd(a,b) S=gcd(a,b),则结果需要乘上 b S \frac bS Sb

code:

#include 
using namespace std;



long long read(){
   long long s=0,w=0;
   char ch=getchar();
   for(;ch<'0'||ch>'9';ch=getchar())s|=ch=='-';
   for(;ch>='0'&&ch<='9';ch=getchar())w=(w<<3)+(w<<1)+(ch^48);
   return s?-w:w;
}



long long n,m,x,y,l,s,X1,Y1;
//前五个为读入的对应变量,后面的X1,Y1为exgcd的解。 



long long exgcd(long long a,long long b,long long &X1,long long &Y1){
	//exgcd求出k。 
	if(!b){
		X1=1;
		Y1=0;
		return a;
	}
	s=exgcd(b,a%b,X1,Y1);
	long long New=X1;
	X1=Y1;
	Y1=New-a/b*Y1;
	return s;
}



int main(){
	
    x=read(),y=read(),m=read(),n=read(),l=read();
    //读入。
	 
    y=x-y,x=n-m;
    //这是A和B。 
    
    if(x<0)x=-x,y=-y;
    //考虑负数。
	 
    exgcd(x,l,X1,Y1);
    //求k。
	 
    if(y%s)puts("Impossible");
    //讨论是否有解。
	 
    else cout<<((X1*(y/s))%(l/s)+(l/s))%(l/s);
    //输出最小解。 
    
	return 0;
	
}

4.乘法逆元。

暂无。

5.同余方程。

暂无。

6.BSGS。

暂无。

7.矩阵乘法。

矩阵乘法是对于两个矩阵之前的操作。
A A A n ∗ x n*x nx的矩阵, B B B x ∗ m x*m xm的矩阵。 C C C为结果矩阵,则 C C C为一个 n ∗ m n*m nm的矩阵。
∀ i ∈ [ 1 , n ] , ∀ j ∈ [ 1 , x ] \forall i\in[1,n],\forall j\in[1,x] i[1,n]j[1,x] C i , j C_{i,j} Ci,j= ∑ k = 1 m A i , k ∗ B k , j \sum_{k=1}^m A_{i,k}*B_{k,j} k=1mAi,kBk,j
对了,矩阵乘法有什么性质呢?


它具有结合律,即 ( A × B ) × C = A × ( B × C ) (A\times B)\times C=A\times (B\times C) (A×B)×C=A×(B×C)
它具有乘法左分配率,即 ( A + B ) × C = A × C + B × C (A+B)\times C=A\times C+B\times C (A+B)×C=A×C+B×C
它具有乘法右分配率,即 C × ( A + B ) = C × A + C × B C\times (A+B)=C\times A+C\times B C×(A+B)=C×A+C×B
它也具有对数乘的结合性,即 c × A × B = A × ( c × B ) c\times A\times B=A\times (c\times B) c×A×B=A×(c×B)
它也可以转置,即 ( A × B ) K = A K × B K (A\times B)^K=A^K\times B^K (A×B)K=AK×BK
但是它不满足交换率,即 A × B = ̸ B × A A\times B=\not B\times A A×B≠B×A
为什么呢?考虑到上面的式子,新的矩阵的每个位置得到的方法是左边矩阵的行乘以右边矩阵的列。当两个矩阵调换位置之后,结果自然就不同了。


或许很多人有疑问了:这个矩阵乘法有什么用呢?其实它非常有用。毕竟它可以加速递推。下面我们根据一道题来了解一下它的用处。

problem1: 斐波那契数列(check here)

这题就是让你推斐波那契第n项,但是n非常大,在long long范围内,直接递推显然会让你痛不欲生,我们可以考虑矩阵。
观察斐波那契递推式: f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2),考虑到一个数只与后两个数有关,所以我们开一个小小的数组 a [ 3 ] a[3] a[3] a [ 1 ] = a [ 2 ] = 1 a[1]=a[2]=1 a[1]=a[2]=1,之后我们要推出 a [ 2 ] , a [ 3 ] a[2],a[3] a[2]a[3],首先 a [ 2 ] a[2] a[2]是有的, a [ 3 ] = a [ 2 ] + a [ 1 ] a[3]=a[2]+a[1] a[3]=a[2]+a[1],因此我们可以开一个二维矩阵来递推。这个矩阵是这样的:
k [ 2 ] [ 2 ] k[2][2] k[2][2]= [ 0 1 1 1 ] \begin{bmatrix}0&1\\1&1\end{bmatrix} [0111]
因此,我们如果需要 f ( n ) f(n) f(n),即 a [ n ] a[n] a[n]的第一个数, a [ n ] = a [ n − 1 ] × k = a [ n − 2 ] × k × k … a[n]=a[n-1]\times k=a[n-2]\times k\times k\dots a[n]=a[n1]×k=a[n2]×k×k
得到 a [ n ] = a [ 1 ] × k n − 1 a[n]=a[1]\times k^{n-1} a[n]=a[1]×kn1,所以呢,我们可以运用快速幂飞快的计算出解。
记得我旁边某位大佬说过:快的不是矩阵乘法,快的是快速幂。其实矩阵乘法之所以可以优化递推,就是因为它具有结合律。
下贴代码:

#include 
using namespace std;
#define mod 1000000007;
//本题模数。 


long long n,a[3]={0,1,1},A[3][3]={{0,0,0},{0,0,1},{0,1,1}};
//a数组为存着的两个斐波那契数。 
//A数组为矩阵。 


void mulself(){
	long long c[3][3];
	memset(c,0,sizeof(c));
	for(int i=1;i<=2;i++)
	    for(int j=1;j<=2;j++)
	        for(int k=1;k<=2;k++)
	            c[i][j]=(c[i][j]+A[i][k]*A[k][j])%mod;
	memcpy(A,c,sizeof(c));
}
//让A矩阵乘以A矩阵。 


void mul(){
	long long c[3];
	memset(c,0,sizeof(c));
	for(int i=1;i<=2;i++)
	    for(int j=1;j<=2;j++)
	            c[i]=(c[i]+a[j]*A[j][i])%mod;
    memcpy(a,c,sizeof(c));
}
//让a数组乘以A矩阵。 


int main(){
	
	cin>>n;--n;
	//执行n-1次。 
	
	for(;n;n>>=1,mulself())if(n&1)mul();
	//快速幂。 
	
	cout<<a[1];
	//输出。
	 
	return 0;
}

其实矩阵并不难,熟能生巧吧。

8.高斯消元。

暂无。

9.线性空间。

我知道这个要高斯消元,现在还没更高斯消元会有些**,凑合一下吧。(会尽早更的啦)。
一般的算法竞赛书上写的有关的介绍都有一些emmm,还是写一下吧:

线性空间是一个关于以下两个运算封闭的向量集合:

1.向量加法 a + b a+b a+b,其中 a , b a,b a,b均为向量。
2.标量乘法 k × a k\times a k×a,也称数乘运算,其中 a a a是向量, k k k是常数(标量)
                               --算法进阶指南
完了,这个也需要一些日子才能更了,又咕了QAQ

10.组合计数。

tm上面还没更完。。。
先更这个吧,毕竟这个还有一些把握。。。
先说一下两个重要的原理:
加法原理:如果完成一件事情有 n n n 类,其中第 i i i 类有 a [ i ] a[i] a[i] 种方法,并且这些方法互不重合,那么完成事情的方案数共 ∑ i = 1 n a [ i ] \sum_{i=1}^na[i] i=1na[i]种。
乘法原理:如果完成一件事情有 n n n 步,其中第 i i i 步有 a [ i ] a[i] a[i] 种方法,并且这些方法互不重合,那么完成事情的方案数共 ∏ i = 1 n a [ i ] \prod_{i=1}^na[i] i=1na[i]种。
或许您会认为这十分简单,但是当您在做数学(雾)的时候,你会发现你认为对的都错了。本蒟蒻深有体会。(350分拿140)
接下来再介绍两个东西:

排列数:从 n n n 个不同的元素中依次取出 m m m 个不同元素排成一列,产生的不同排列的数量为:
P n m = n ! ( n − m ) ! = ∏ i = 1 m n − m + i P_n^m=\dfrac {n!}{(n-m)!}=\prod_{i=1}^mn-m+i Pnm=(nm)!n!=i=1mnm+i
组合数:从 n n n 个不同元素中每次取出 m m m 个不同元素,组合成一个集合(不考虑顺序),产生的不同集合数量为:
C n m = n ! m ! ( n − m ) ! = ∏ i = 1 m n − m + i ∏ i = 1 m i C_n^m=\dfrac{n!}{m!(n-m)!}=\dfrac{\prod_{i=1}^mn-m+i}{\prod_{i=1}^mi} Cnm=m!(nm)!n!=i=1mii=1mnm+i
性质:

  • C n m = C n n − m C_n^m=C_n^{n-m} Cnm=Cnnm
  • C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1
  • ∑ i = 1 n C n i = 2 n \sum_{i=1}^nC_n^i=2^n i=1nCni=2n

证明:

考虑到我们取出 m m m 个数后,剩下的 n − m n-m nm 个数也组成了一个集合,所以对于所有的方案,都有一个 n − m n-m nm 个元素的集合与之对应,故 C n m = C n n − m C_n^m=C_n^{n-m} Cnm=Cnnm


我们在 n n n 个数中取 m m m 个,当我们在考虑是否选取第 n n n 个的时候,方案数等于选到了第 n − 1 n-1 n1 个的时候剩下了一个位置给第n个元素 + + + 我们选到了第 n − 1 n-1 n1 个的时候没剩下位置给第n个元素。即 C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1


∑ i = 1 n C n i \sum_{i=1}^nC_n^i i=1nCni即为在n个元素的集合里选取随意多的数,换个思路,就是每个元素可以选择或者不选择,答案即为 2 n 2^n 2n 个,故 ∑ i = 1 n C n i = 2 n \sum_{i=1}^nC_n^i=2^n i=1nCni=2n


这便是以上三条性质的证明。
现在我们讨论讨论组合数的求法,看第二条性质不难想到利用递推,为了避免爆炸,我们可以用质因数相减思路。
因此有了二项式定理。

1.二项式定理:

( a + b ) n = ∑ k = 0 n C n k × a k × b n − k (a+b)^n=\sum_{k=0}^nC_n^k\times a^k\times b^{n-k} (a+b)n=k=0nCnk×ak×bnk

矩阵形式: ( a + b ) n = [ a 0 … a n ] × [ C n 0 ⋱ C n n ] × [ 1 ⋮ 1 ] × [ b 0 ⋮ b n ] (a+b)^n=\begin{bmatrix}a^0&\dots& a^n\end{bmatrix}\times \begin{bmatrix}C_n^0\\&\ddots&\\& &C_n^n\end{bmatrix}\times \begin{bmatrix}& & 1\\& \vdots\\1\end{bmatrix}\times \begin{bmatrix}b^0\\\vdots\\b^n\end{bmatrix} (a+b)n=[a0an]×Cn0Cnn×11×b0bn, n ∈ N + n\in\N^+ nN+

证明:

n = 1 n=1 n=1 时, ( a + b ) 1 = C n 0 × a 0 × b n − k = a + b (a+b)^1=C_n^0\times a^0\times b^{n-k}=a+b (a+b)1=Cn0×a0×bnk=a+b成立。
假设当 n = m n=m n=m时命题成立,设 n = m + 1 n=m+1 n=m+1,则有:
  ( a + b ) m + 1 (a+b)^{m+1} (a+b)m+1
 
= a ( a + b ) m + b ( a + b ) m a(a+b)^m+b(a+b)^m a(a+b)m+b(a+b)m
 
= ( a + b ) ∑ k = 0 m C m k × a k × b m − k (a+b)\sum_{k=0}^mC_m^k\times a^k\times b^{m-k} (a+b)k=0mCmk×ak×bmk

= ∑ k = 0 m C m k × a k + 1 × b m − k + ∑ k = 0 m C m k × a k × b m − k + 1 \sum_{k=0}^mC_m^k\times a^{k+1}\times b^{m-k}+\sum_{k=0}^mC_m^k\times a^k\times b^{m-k+1} k=0mCmk×ak+1×bmk+k=0mCmk×ak×bmk+1

= ∑ k = 1 m + 1 C m k − 1 × a k × b m − k + 1 + ∑ k = 0 m C m k × a k × b m − k + 1 \sum_{k=1}^{m+1}C_m^{k-1}\times a^k\times b^{m-k+1}+\sum_{k=0}^mC_m^k\times a^k\times b^{m-k+1} k=1m+1Cmk1×ak×bmk+1+k=0mCmk×ak×bmk+1

= ∑ k = 0 m + 1 ( C m k − 1 + C m k ) × a k × b m − k + 1 \sum_{k=0}^{m+1}(C_m^{k-1}+C_m^k)\times a^k\times b^{m-k+1} k=0m+1(Cmk1+Cmk)×ak×bmk+1

= ∑ k = 0 m + 1 C m + 1 k × a k × b m + 1 − k \sum_{k=0}^{m+1}C_{m+1}^k\times a^k\times b^{m+1-k} k=0m+1Cm+1k×ak×bm+1k
证毕。
现在就去尝试一下这个吧!

problem1:计算系数(check here)

这题题目简洁明了,给一个多项式 ( a x + b y ) k (ax+by)^k (ax+by)k,求多项式展开后 x n × y m x^n\times y^m xn×ym项的系数,对10007取模。
范围: 0 ≤ n , m ≤ k ≤ 1000 , n + m = k , 0 ≤ a , b ≤ 1 0 6 0\le n,m\le k\le1000,n+m=k,0\le a,b\le 10^6 0n,mk1000,n+m=k,0a,b106
其实就是一道简简单单的题,考据二项式定理,就可以知道 x n × y m x^n\times y^m xn×ym 的系数是 C k n × a n × b m C_k^n\times a^n\times b^m Ckn×an×bm ,然后就可以直接计算了,对于中间的除法直接利用逆元即可。
下贴代码:

#include 
using namespace std;



#define mod 10007
long long a,b,k,n,m,S;
//模数,题目中对应的变量。 



long long ksm(long long x,long long y,long long z=1){
	for(;y;y>>=1,x=x*x%mod)if(y&1)z=z*x%mod;
	return z;
}
//快速幂 



long long C(long long x,long long y){
	long long s=1;
	
	for(long long i=1;i<=x;++i)
	    s=s*i%mod;
	//计算分子。 
	 
	long long s1=1;
	
	for(long long i=1;i<=y;++i)
	    s1=s1*i%mod;
	for(long long i=1;i<=x-y;++i)
	    s1=s1*i%mod;
	//计算分母的积。
	 
	s=s*ksm(s1,mod-2)%mod;
	//对分母的积统一求逆元,计算答案。
	 
	return s;
}
//计算组合数 



int main(){
	
	scanf("%lld %lld %lld %lld %lld",&a,&b,&k,&n,&m);
	//读入。
	 
	S=ksm(a,n)*ksm(b,m)%mod;
	S=S*C(k,n)%mod;
	//计算答案。
	 
	printf("%lld",S);
	
	return 0;
}

2.Lucas定理:

C n m = C n   m o d   p m   m o d   p ∗ C n / p m / p C_n^m=C_{n\bmod p}^{m\bmod p}*C_{n/p}^{m/p} Cnm=CnmodpmmodpCn/pm/p

证明:

首先我们需要证明三个结论。


  • C p i ≡ p i C p − 1 i − 1 ≡ 0 ( m o d p ) , ( 1 ≤ i ≤ p − 1 ) C_p^i\equiv \dfrac piC_{p-1}^{i-1}\equiv0\pmod p,(1\le i\le p-1) CpiipCp1i10(modp),(1ip1)
  • ( 1 + x ) p ≡ 1 + x p ( m o d p ) (1+x)^p\equiv 1+x^p\pmod p (1+x)p1+xp(modp)
  • z = a p + b z=ap+b z=ap+b,则 ( 1 + x ) z ≡ ( 1 + x p ) a × ( 1 + x ) b (1+x)^z\equiv (1+x^p)^a\times (1+x)^b (1+x)z(1+xp)a×(1+x)b

证明:
C p i = p ! i ! × ( p − i ) ! = p i × ( p − 1 ) ! ( i − 1 ) ! × ( p − i ) ! = p i C p − 1 i − 1 C_p^i=\dfrac {p!}{i!\times (p-i)!}=\dfrac pi\times \dfrac {(p-1)!}{(i-1)!\times (p-i)!}=\dfrac piC_{p-1}^{i-1} Cpi=i!×(pi)!p!=ip×(i1)!×(pi)!(p1)!=ipCp1i1

观察 C p i = p ! i ! × ( p − i ) ! C_p^i=\dfrac {p!}{i!\times(p-i)!} Cpi=i!×(pi)!p!,考虑到 i ! × ( p − i ) ! i!\times (p-i)! i!×(pi)!,这两个数都比 p p p 小,由于 p p p 是质数,故该式子中仅
有分子有p这个因子,故 C p i ≡ p i C p − 1 i − 1 ≡ 0 ( m o d p ) C_p^i\equiv \dfrac piC_{p-1}^{i-1}\equiv0\pmod p CpiipCp1i10(modp)

( 1 + x ) p = ∑ k = 0 p C n k × a k × b p − k (1+x)^p=\sum_{k=0}^pC_n^k\times a^k\times b^{p-k} (1+x)p=k=0pCnk×ak×bpk

为了方便观察这边将它改为繁琐些的式子。

     ( 1 + x ) p ~~~~(1+x)^p     (1+x)p

= C p 0 × x 0 × 1 p + C p − 1 1 × x 1 × 1 p − 1 +    …    + C p p × x n × 1 0 =C_p^0\times x^0\times 1^p+C_{p-1}^1\times x^1\times 1^{p-1}+~~\dots~~+C_p^p\times x^n\times 1^0 =Cp0×x0×1p+Cp11×x1×1p1+    +Cpp×xn×10

根据上面的结论 1 1 1 ,我们可以得知,仅有 C p 0 × x 0 × 1 p + C p p × x n × 1 0 C_p^0\times x^0\times 1^p+C_p^p\times x^n\times 1^0 Cp0×x0×1p+Cpp×xn×10是有效的,故原式

≡ C p 0 × x 0 × 1 p + C p p × x n × 1 0 ( m o d p ) \equiv C_p^0\times x^0\times 1^p+C_p^p\times x^n\times 1^0\pmod p Cp0×x0×1p+Cpp×xn×10(modp)

≡ 1 + x p \equiv 1+x^p 1+xp
有了以上两个结论,再来证明第三个就非常简单。

∵ ( 1 + x ) z = ( 1 + x ) a p × ( 1 + x ) b \because(1+x)^z=(1+x)^{ap}\times (1+x)^b (1+x)z=(1+x)ap×(1+x)b

( 1 + x ) a p ≡ ( ( 1 + x ) p ) a ≡ ( 1 + x p ) a (1+x)^{ap}\equiv((1+x)^p)^a\equiv(1+x^p)^a (1+x)ap((1+x)p)a(1+xp)a

∴ ( 1 + x ) z ≡ ( 1 + x p ) a × ( 1 + x ) b \therefore(1+x)^z\equiv(1+x^p)^a\times (1+x)^b (1+x)z(1+xp)a×(1+x)b

我们接着往下探索:
设一个数 n = i p + j n=ip+j n=ip+j
C a n × x n C_a^n\times x^n Can×xn有什么方法改的好看一些呢 q w q qwq qwq
其实有,只不过更丑了
考虑到我们已经知道了 ( 1 + x ) z ≡ ( 1 + x p ) a × ( 1 + x ) b ( m o d p ) (1+x)^z\equiv(1+x^p)^a\times (1+x)^b\pmod p (1+x)z(1+xp)a×(1+x)b(modp),大力提出两边式子里n的系数,可以发现左边为 C a n C_a^n Can ,右边则为 C a i × C b j C_a^i\times C_b^j Cai×Cbj,为什么呢?因为 b b b 是由一个数   m o d     p \bmod~p mod p得来的,那么 b ≤ q − 1 b\le q-1 bq1,所以为了让两边系数相等, i × p i\times p i×p这个系数必须去 ( 1 + x p ) a (1+x^p)^a (1+xp)a里面找,而 j j j 这个系数则必须去 ( 1 + x ) b (1+x)^b (1+x)b里找。
所以
C a n × x n ≡ C a i × x i × p × C b j × x j C_a^n\times x^n\equiv C_a^i\times x^{i\times p}\times C_b^j\times x^j Can×xnCai×xi×p×Cbj×xj

C a n × x n ≡ C a i × C b j × x n C_a^n\times x^n\equiv C_a^i\times C_b^j\times x^n Can×xnCai×Cbj×xn

C a n ≡ C a i × C b j C_a^n\equiv C_a^i\times C_b^j CanCai×Cbj

或许证完了。
又得等一段时间???

11. M o ¨ b i u s M\ddot{o}bius Mo¨bius函数。

又很开心了 q w q qwq qwq ,没更容斥先更莫比。。。
莫比乌斯反演是数论数学中很重要的内容,可以用于解决很多组合数学的问题。
–百度百科

一开始比较迷(虽然现在也是。。。),但是题目做多了,套路感觉就来了。
首先查了一下莫比乌斯反演,那些百科上面对莫比乌斯反演的介绍真是面(又)面(臭)俱(又)到(长)。
首先我们引入莫比乌斯函数μ
它的定义是这样的:

  • 如果 x x x 只能分解为偶数个不同质数的乘积时,(当 x = 1 x=1 x=1 时算无法分解,即偶数个不同质数之积) μ [ x ] = 1 μ[x]=1 μ[x]=1
  • 如果 x x x 只能分解为奇数个不同质数的乘积时, μ [ x ] = − 1 μ[x]=-1 μ[x]=1
  • 除去以上两种情况, μ [ x ] = 0 μ[x]=0 μ[x]=0
    或许你会有疑问了:我要 μ μ μ 有何用?
    它不仅用处大,逼格也大,有许许多多函数与它相关,梅滕斯函数,与生成函数,与无穷极数等   …   ~\dots~   
    慢慢了解它吧!

你可能感兴趣的:(数论)