基础数论学习记录

一、按位异或的应用:

        例题1:给定2n+1个数,其中有一个数只出现了一次,找出这个数。

        由于两个相同的数字异或等于0,一个数异或0等于他自己。

        于是从1开始异或到2n+1即可。剩下的数字就是那个出现一次的数。

        例题2:给定2n+2个数,其中有两个数是单身,按从大到小顺序找到这两个数。内存限制只能开一个2n+2的数组。

        贴一个代码:

#include <iostream>
using namespace std;
const int MAX=100000+10;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s*10)+ch-'0';ch=getchar();}
    x=s*w;
}

int a[MAX];
int main()
{
	int x;
	int sum=0;int n;cin>>n;
	for(int i=0;i<2*n;i++)
	{
		read(x);
		a[i]=x;
		sum^=a[i];
	}
	int lowbit= sum & (-sum);
	
	int b=0,c=0;
	for(int i=0;i<2*n;i++)
	{
		if((lowbit&a[i])==lowbit)
		{
			b^=a[i];
		}else
		{
			c^=a[i];
		}
	}
	if(b<c)
	{
		cout<<b<<" "<<c<<endl;
	}else
	{
		cout<<c<<" "<<b<<endl;
	}
	return 0;
} 

        不难看出从1异或到2n+2得到的就是两个单身的数字的异或,叫这个数为sum吧。

        可以随意找sum二进制的一位:只要它是1,那么就可以判断出来两个单身数字在这一位上必有一个是1,一个是0。然后根据这一位是1还是0在2n+2个数字里分成两份。这样问题就转化为在每一份里找到一个2n+1的数,根据例题1可以解决。

        技巧:lowbit的应用,lowbit就是找到一个数二进制最小为1的位,比如说为n。lowbit数值上等于2的n次幂。比如说11111000,lowbit就是1000;

        lowbit的求法 lowbit(a)=a &(-a);得到了lowbit,就可以通过按位与操作来判断数字的二进制第n位是不是1。

二、快速幂与矩阵快速幂:

        P1226 【模板】快速幂 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

        洛谷模板题.

       快速幂的复杂度是O(logn),a的n次幂,如果n是奇数,那么a的n次幂等于a的n-1次幂。如果n是偶数,a的n次幂等于a方的n/2次幂。每一步都去取模。

#include <iostream>
using namespace std;
#define ll long long
ll quickpow(ll x,ll y,ll mod)
{
	ll re=1;
	while(y!=0)
	{
		if(y%2==1) re=re*x%mod;
		
		x=x*x%mod,y=y/2;//2的y次方等于4的y/2次方.	
	}
	return re;
}
int main()
{
	ll x,y,mod;
	cin>>x>>y>>mod;
	//2^10 mod 9=7
	cout<<x<<"^"<<y<<" mod "<<mod<<"="<<quickpow(x,y,mod)<<endl;
	return 0;
}

        矩阵快速幂

        即把一个复杂递推式换成矩阵的形式Ax=B,n次递推只需要求出矩阵x的n次幂再乘以A即可。

        P1962 斐波那契数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <iostream>
using namespace std;
#define ll long long
const ll mod=1e9+7;
struct matrix
{
	ll sqr[2][2];
}a,f,x;

matrix operator*(matrix a,matrix b)
{
	matrix c;
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		{
			//第i行乘第j列 
			c.sqr[i][j]=(a.sqr[i][0]%mod*b.sqr[0][j]%mod)%mod+(a.sqr[i][1]%mod*b.sqr[1][j]%mod)%mod;
             c.sqr[i][j]%=mod;
		}
		return c;
}

void quickpow(matrix &x,ll y)
{
	matrix I;
	I.sqr[0][0]=I.sqr[1][1]=1;
	I.sqr[0][1]=I.sqr[1][0]=0;
	while(y)
	{
		if(y%2==1) I=I*x;
		
		x=x*x,y/=2;
	}
	x=I;
}

int main()
{
	f.sqr[0][0]=2;f.sqr[0][1]=1;
	x.sqr[0][1]=x.sqr[0][0]=x.sqr[1][0]=1;
	ll n;cin>>n;
	quickpow(x,n-1);
	f=x*f;
	cout<<f.sqr[0][1]%mod<<endl;
	return 0;
}

        矩阵快速幂和快速幂实现上的区别:矩阵快速幂的re需要设为单位矩阵I,矩阵乘法需要重载运算符*,即aij的数等于第i行*第j列每个相应的数相乘再相加。这样效率比单纯递推高。

三、素数相关:

        线性筛:

        P3383 【模板】线性筛素数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <iostream>
using namespace std;
const int MAX=1e7+10;
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s*10)+ch-'0';ch=getchar();}
    x=s*w;
}

bool judge[MAX];//0是素数,1不是素数
int prime[MAX];int p=0;
int main()
{
	int n;read(n);
	for(int i=2;i<n;i++)
	{
		if(!judge[i]) prime[++p]=i;
		for(int j=1;j<=p&&i*prime[j]<=n;j++)
		{
			judge[i*prime[j]]=1;
			if(i%prime[j]==0) break;//相比于埃氏筛优化的地方 
		}
	}
	int t,x;
	read(t);
	for(int i=0;i<t;i++)
	{
		read(x);
		printf("%d\n",prime[x]);
	}
	return 0;
	
}

        线性筛就是遍历1到i,查询i之前的所有素数,这样i*n一定不是素数,如果遍历到i的时候i没有被前面的数标记,那就把i存到素数数组里。如果某个素数是i的约数,break;不重复计算。

  欧拉函数求解:

        给定一个范围 n,有 q 个询问,每次询问一个数的欧拉函数的值:

#include <iostream>
using namespace std;
const int MAX=1e8+10;
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s*10)+ch-'0';ch=getchar();}
    x=s*w;
}
bool judge[MAX];//0是素数,1不是素数
int prime[MAX];int p=0;
int phi[MAX];//欧拉函数 
int main()
{
    phi[1]=1;
	int n;read(n);
	for(int i=2;i<n;i++)
	{
		if(!judge[i]) prime[++p]=i,phi[i]=i-1;
		for(int j=1;j<=p&&i*prime[j]<=n;j++)
		{
			judge[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
			
				phi[i*prime[j]]=phi[i]*prime[j];//两个数同余的合数的欧拉函数:余数(质数)乘以素数 
				break;//相比于埃氏筛优化的地方 
			}else
			{
				phi[i*prime[j]]=phi[i]*phi[prime[j]];//互为素数的合数的欧拉函数:两素数欧拉函数之积
			}
		}
	}
	
	int t;read(t);int x;
	for(int i=0;i<t;i++)
	{
		read(x);
		printf("%d\n",phi[x]);
	}
	return 0;
	
}

        欧拉函数phi(x)指的是从1到x里与x互质元素的个数。

        欧拉函数是通过三条定理来推的

        素数的欧拉函数是p-1(1和任何数都互素)- i为素数

        nm互素,phi(nm)=phi(n)*phi(m) 

        m%n==0 phi(nm)==n*phi[m]

 

           

  欧拉函数应用:

        训练题

        这个题求得是在一个n*n的正方形里,gcd(x,y)=1的数对个数。
        在数值上等于2*欧拉函数的前缀和-1;

#include <iostream>
using namespace std;
const int MAX=1e8+10;
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s*10)+ch-'0';ch=getchar();}
    x=s*w;
}
bool judge[MAX];//0是素数,1不是素数
int prime[MAX];int p=0;
int phi[MAX];//欧拉函数 
int main()
{
    phi[1]=1;
	int n;read(n);
	for(int i=2;i<=n;i++)
	{
		if(!judge[i]) prime[++p]=i,phi[i]=i-1;
		for(int j=1;j<=p&&i*prime[j]<=n;j++)
		{
			judge[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
			
				phi[i*prime[j]]=phi[i]*prime[j];//两个数同余的合数的欧拉函数:余数(质数)乘以素数 
				break;//相比于埃氏筛优化的地方 
			}else
			{
				phi[i*prime[j]]=phi[i]*phi[prime[j]];//互为素数的合数的欧拉函数:两素数欧拉函数之积
			}
		}
	}
	
	int t;cin>>t;int x;
    long long int ans=0;
	for(int i=1;i<=n;i++)
	{
        //cout<<phi[i]<<endl;
        ans+=phi[i];
	}
    cout<<2*ans-1<<endl;
	return 0;
	
}
     
           
 
四、同余相关:

                扩展欧几里得算法:

                        

#include<iostream>
using namespace std;
#define ll long long 
int t,c,d,x,y;
void exgcd(int a, int b, int &x, int &y)
{
    if(a%b==0)
    {
        x=0;
        y=1;
        return;
    }//先递推找到一个可以得到通解的一组数字。 
    exgcd(b,a%b,x,y);//递推,如果a取余b不等于0不会返回。 
    t=x;
    x=y;
    y=t-a/b*y;
} //最后得到的一组x,y才是解 
int main()
{
    ll c,d;
   cin>>c>>d;
    exgcd(c,d,x,y);//一定有解就不用管xy初始的值。 
    while(x<=0)
    {
        x+=d;
    }//最小正整数解保证是正数 
    cout<<x<<endl;
    return 0;
}                                                                                                                                                 

   求关于 x 的同余方程 ax≡1(modb) 的最小正整数解x。

        转化为ax+by等于1的ex欧几里得算法

          结果是x的最小整数;y是个负数

  乘法逆元求法:

        P2613 【模板】有理数取余 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

        逆元就是a/b(modp)=a*b'(modp) b'就是b的逆元,

#include <iostream>
using namespace std;
#define ll long long
const ll mod=1e9+7; 
ll getint(){
    char chr=getchar();ll x=0;
    while(chr<'0'||chr>'9')chr=getchar();
    while(chr>='0'&&chr<='9'){
        x=((x<<3)+(x<<1)+(chr&15))%mod;//15的二进制位是00001111;位运算更快,实质上是把x*10; 
        chr=getchar();
    }
    return x;
}

ll quickpow(ll x,ll y,ll mod)
{
	ll re=1;
	while(y!=0)
	{
		if(y%2==1) re=re*x%mod;
		
		x=x*x%mod,y=y/2;//2的y次方等于4的y/2次方.	
	}
	return re;
}
int main()
{
	ll a,b;
	a=getint();b=getint();
	if(b==0) cout<<"Angry!"<<endl;
	else cout<<a*quickpow(b,mod-2,mod)%mod<<endl;
	return 0;
}

求n/m modp

转化为求n*m的逆元modp;

求逆元的几种方法:
1.exgcd求ax=1(modb)的解,求出逆元

2.费马小定理,a的p-1次方与1同余。a的p-2次方就是逆元

3.递推公式

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值