NOIP模板复习——数论

最大公约数与最小公倍数

int gcd(int a,int b)
{
	int r=a%b;
	while(r!=0)
	{
		a=b;
		b=r;
		r=a%b;
	}
	return b;
}
int lcm(int a,int b)
{
	return a*b/gcd(a,b);
}

快速幂&快速积

快速积的主要作用是防止乘数过大,炸 long long 范围

int Multiply(int a,int b)
{
	int ans=0;
	while(b)
	{
		if(b&1)
		  ans=(ans+a)%Mod;
		a=(a+a)%Mod;
		b>>=1;
	}
	return ans;
}
int Power(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1)
		  ans=Multiply(ans,a);
		a=Multiply(a,a);
		b>>=1;
	}
	return ans;
}

线性筛

p r i m e prime 是素数集, m a x max 是最大质因数, m i n min 是最小质因数, p h i phi 是欧拉函数, m a r k mark 是标记一个数是否为素数

int prime[N],Max[N],Min[N],phi[N];
bool mark[N];
void linear_sieves()
{
	int i,j,sum=0;
	memset(mark,true,sizeof(mark));
	mark[0]=mark[1]=false;phi[1]=1;
	for(i=2;i<N;++i)
	{
		if(mark[i])
		{
			phi[i]=i-1;
			prime[++sum]=i;
			Min[i]=Max[i]=i;
		}
		for(j=1;j<=sum&&i*prime[j]<N;++j)
		{
			mark[i*prime[j]]=false;
			Max[i*prime[j]]=Max[i];
			Min[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else  phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

质因数分解

这个要用到线性筛素数( s u m sum 是筛出的素数个数)

听说复杂度是 O ( n ln n ) O(\frac{\sqrt n}{\ln n}) (因为 n n 以内的素数约有 n ln n \frac{n}{\ln n} 个,所以 n \sqrt n 内就有 n ln n \frac{\sqrt n}{\ln \sqrt n} 个,而 ln n = 2 ln n \ln n=2\ln \sqrt n ),不过只用记住它比较小就行了

void divide(int x)
{
	int i;
	for(i=1;i<=sum&&prime[i]*prime[i]<=x;++i)
	{
		while(x%prime[i]==0)
		{
			x/=prime[i];
			printf("%d ",prime[i]);
		}
	}
	if(x!=1)  printf("%d",x);
}

扩展欧几里得

void exgcd(int a,int b,int &x,int &y)
{
	if(!b)
	{
		x=1,y=0;
		return;
	}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}

逆元

快速幂(要求模数必须为质数)

inv 是逆元,x 是要求逆元的数,Power是快速幂,Mod 是模数

inv=Power(x,Mod-2);

扩展欧几里得(要求 x 必须与 Mod 互质)

exgcd(x,Mod,inv,y);
inv=(inv+Mod)%Mod;

Ps:快速幂和扩欧的模板就去上面看啦

线推

inv[1]=1;
for(i=2;i<=n;++i)
    inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;

组合数

O ( n 2 ) O(n^2) 递推组合数(方便取模)

C[n][m] 表示 C n m C_{n}^{m}

C[0][0]=1;
for(i=1;i<=n;++i)
{
	C[i][0]=1;
	for(j=1;j<=i;++j)
	    C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod;
}

线性筛出阶乘和逆元, O ( 1 ) O(1) 求组合数(Power 是快速幂,求逆元用的)

f a c fac 是阶乘, i n v inv 是阶乘的逆元

void init()
{
	int i;
	fac[0]=fac[1]=1;
	for(i=2;i<=n;++i)  fac[i]=1ll*fac[i-1]*i%Mod;
	inv[n]=Power(fac[n],Mod-2);
	for(i=n-1;i>=1;--i)  inv[i]=1ll*inv[i+1]*(i+1)%Mod;
}
int C(int n,int m)
{
	return 1ll*fac[n]*inv[m]%Mod*inv[n-m]%Mod;
}

高斯消元

a[i][1]~a[i][n] 是各个元的系数, a[i][n+1] 是该计算出来的值

void Guass()
{
	int i,j,k;
	for(i=1;i<=n;++i)
	{
		k=i;
		for(j=i+1;j<=n;++j)
		  if(fabs(a[k][i])<fabs(a[j][i]))
		    k=j;
		swap(a[i],a[k]);
		for(j=i+1;j<=n;++j)
		  for(k=i+1;k<=n+1;++k)
		    a[j][k]-=a[i][k]*a[j][i]/a[i][i];
	}
	for(i=n;i;--i)
	{
		for(j=i+1;j<=n;++j)
		  a[i][n+1]-=ans[j]*a[i][j];
		ans[i]=a[i][n+1]/a[i][i];
	}
}

矩阵快速幂

这里就把大致的框架打出来(代码中是 2 2 2*2 的矩阵)

struct matrix
{
	int m[N][N];
	matrix(int t=0)
	{
		memset(m,0,sizeof(m));
		for(int i=1;i<=2;++i)  m[i][i]=t;
	}
	friend matrix operator * (const matrix &a,const matrix &b)
	{
		int i,j,k;
		matrix c(0);
		for(i=1;i<=2;++i)
		  for(j=1;j<=2;++j)
		    for(k=1;k<=2;++k)
		      c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
		return c;
	}
	friend matrix operator ^ (matrix a,long long b)
	{
		matrix c(1);
		for(;b;b>>=1,a=a*a)
		  if(b&1)
		    c=c*a;
		return c;
	}
};

康拓展开

康拓展开和逆康拓展开( f a c fac 是预处理出来的阶乘)

ll cantor()
{
	ll ans=0;
	for(int i=1;i<=n;++i)
	{
		int num=0;
		for(int j=i+1;j<=n;++j)
		  if(a[j]<a[i])  num++;
		ans+=fac[n-i]*num;
	}
	return ans;
}
void reverse_cantor(ll x)
{
	int i,j;x--;
	memset(vis,false,sizeof(vis));
	for(i=1;i<=n;++i)
	{
		int num=x/fac[n-i];
		for(j=1;j<=n;++j)
		  if(!vis[j]&&!num--)
		    break;
		a[i]=j;
		vis[j]=true;
		x%=fac[n-i];
	}
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/83793581