2021-07-30

学了一会数论,好难

1.乘法逆元:a/b % p ,若a/b在进行取模运算时,会出现精度问题,而且模运算对除法不适用,(没有分配律,大概就这意思)而求出乘法逆元后,可以把原式变为a*x % p的形式,且值不变。a*x ≡ 1 (mod p)中,a,p为已知量,则x为a的乘法逆元。例题:乘法逆元

设 p=k*i+r,(1

再将这个式子放到\pmod p(modp)意义下就会得到:

k*i+r \equiv 0 \pmod pk∗i+r≡0(modp)

然后乘上i^{-1}i−1,r^{-1}r−1就可以得到:

k*r^{-1}+i^{-1}\equiv 0 \pmod pk∗r−1+i−1≡0(modp)

i^{-1}\equiv -k*r^{-1} \pmod pi−1≡−k∗r−1(modp)

i^{-1}\equiv -\lfloor \frac{p}{i} \rfloor*(p \bmod i)^{-1} \pmod pi−1≡−⌊ip​⌋∗(pmodi)−1(modp)

详见这里

//板子
//递推线性时间求乘法逆元 
#include
using namespace std;
const int N=20000530;
int n,p,inv[N];
int main()
{
	cin>>n>>p;
	inv[1]=1;
	for(int i=2;i<=n;i++)
	{
		inv[i]=(long long)(p-p/i)*inv[p%i]%p;
        
	}
	for(int i=1;i<=n;i++)cout<

2.费马小定理:a^(p-1) ≡ 1 (mod p)p是质数,可以用来求单次乘法逆元,由费马小定理 a^(p-1)≡1 , 变形得 a * a^(p-2)≡1(mod p);若a,p互质,因为a * a^(p-2)≡1(mod p)且a*x≡1(mod p),则x=a^(p-2) (mod p),用快速幂可快速求之。

3.拓展欧几里得算法,可以对ax+by=c这样的不定方程求解,a,b,c为整数。若要使方程有解,则必须满足gcd(a,b)|c, (即c%gcd(a,b)=0)。 而在题目中一般会让我们求x的最小正整数解,只需要解出其中一组解,x可能非常大或为负数,x=(x%b+b)%b;给x批量加上b,可以保持ax+by=1成立,且满足x为最小正整数解

例题:同余方程

#include
using namespace std;
//设置为全局变量,在递归栈中返回时更改
//还有一种写法,写在函数内,调用地址,大概就这样,int& x 
long long x,y;
void exgcd(long long a,long long b)
{
	//要求ax+by=gcd(a,b)成立 
	//可以通过x1,y1推出x0,y0,层层递归
	//当b=0时,方程必有解,也是递归的出口 
	if(b==0)//特殊解 
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	
	long long tx=x;//暂存 
	x=y;
	y=tx-a/b*y;//有证明的,在题解里面QWQ,背一下也蛮香的 
}
int main()
{
	long long a,b;
	cin>>a>>b;
	exgcd(a,b);//求出x的一组解 
	
	x=(x%b+b)%b;//x可能为负数,或一个很大的值,给x批量加上b,可以找到最小正整数解 
	cout<

青蛙的约会

#include
using namespace std;
long long c,x,y,m,n,l,g;

void exgcd(long long a,long long b)
{
	if(b==0)//递归出口时a必为gcd(a,b) 
	{
		x=1;
		y=0;
		g=a;//gcd(a,b)
		return;
	}
	exgcd(b,a%b);
	long long tx=x;
	x=y;
	y=tx-a/b*y;
	
}
int main()
{
	scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&l);
	//写成ax+by=c的形式
	long long a=n-m,b=l;
	c=x-y;

	if(a<0)a=-a,c=-c;//a,b一定是正的好像因为gcd中的a,b要是正的 。x,y可以是负的
	
	exgcd(a,b);//先求出ax+by=gcd(a,b)的一组解 
	
	if(c%g)
	{
		cout<<"Impossible";
		return 0;
	 } 
	
	x=x*c/g;//x为ax+by=c的一组解 c=k*gcd(a,b)
	x=(x%(b)+(b))%(b/g);//最小正整数解
	//b/g 是最小单元,最后那个必须模b,否则会wa掉两个点
	//如 x=(x%(b)+(b))%(b);是不行的 
	cout<

4.中国剩余定理:例题:猜数字(板子题),(定理的正确性其实很好证的,就是数字有点多),数据有坑点,①bi<=10^18,直接相乘可以爆long long,

,所以必须要用快速乘(简单来说一点点乘),②a[i]可能为负数,要把它转为正,(不确定就转正数吧,毕竟我也搞不太清楚)

#include
#include
#define ll long long
ll a[13],b[13],k,M=1,x,y,ans=0;
using namespace std;
ll ksc(ll x,ll y,ll mod)
{
	ll tmp=0;
	for(;y;y>>=1,x=x*2%mod)if(y&1)tmp=(tmp+x)%mod;
	//b的末尾为1,则对答案有贡献 
	return tmp;
}
void exgcd(ll a,ll b)
{
	if(b==0)
	{
		x=1;y=0;return;
	}
	exgcd(b,a%b);
	ll tx=x;
	x=y;
	y=tx-a/b*y;
}
int main()
{
	cin>>k;
	for(int i=1;i<=k;i++)cin>>a[i];
	for(int i=1;i<=k;i++)
	{
		cin>>b[i];M*=b[i];
	}
	for(int i=1;i<=k;i++)a[i]=(a[i]%b[i]+b[i])%b[i];
	for(int i=1;i<=k;i++)
	{
		ll mi=M/b[i];
		exgcd(mi,b[i]);//前面那个是n-1个数之积,后面那个数,就一个数 
		ll ti=(x%b[i]+b[i])%b[i];//exgcd求乘法逆元,这里gcd(a,b)=1,所以b不用写成b/gcd(a,b) 
		ans=(ans+ksc(mi,ksc(a[i],ti,M),M))%M;
	}
	printf("%lld",ans);
	return 0;
 } 

5.欧拉定理,埃氏筛法,线性筛,区间筛,光速幂。。。以后来填坑

6.几个优秀的题解,讲解1,讲解2,讲解3, 中国剩余定理

你可能感兴趣的:(2021-07-30)