【week1day4笔记】数论初步

素数相关
(1)欧拉筛法
欧拉筛法是竞赛中常用的素数筛法,但实际情况下其效率可能仍不能满足竞赛中苛刻的数据与时间要求,对于初学者而言是一种很有效的求素数手段。
欧拉筛法基本思路:
1、默认所有数字都是素数,0,1除外
2、从2开始,若此数字被认为是素数,将其存入一个用于存储素数的数组。
3、扫描整个存储数组(包括刚刚存入的素数),将当前数字与扫描到的素数的乘积标记为非素数

void Get_prime()
{
 memset(Is_prime,true,sizeof(Is_prime));
 Is_prime[0]=Is_prime[1]=false;
 for(int i=2;i<=10000;i++)
 {
  if(Is_prime[i])prime[len++]=i;
  for(int j=0;j<len&&prime[j]*i<=10000;j++)Is_prime[prime[j]*i]=false;
 }
}

(2)反质数
定义: 对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。
如果某个正整数x满足:g(x)>g(i) (0 < i < x),则称x为反质数。例如,整数1,2,4,6等都是反质数。
即是区间中约数最多的那个数

性质:
1.一个反素数的质因子必然是从2开始连续的质数
2.N=p1a1 *p2a2 *…*pnan,必然有a1>=a2>=a3…>=an

容斥原理
在计数时,为了防止重复计算,先不考虑重复情况,将所有对象数量按特定方法计算出来,再计算该计算方法下的重复部分,进行去除。在二次去除中仍存在可能出现重复的情况,因此可能仍要进行第三次、第四次去重……这个过程中应用到的便是容斥原理。
容斥原理基本公式:|A + B + C| = |A| + |B| + |C| - |AB| - |AC| - |BC| + |ABC|
可以继续以此类推,按加减加减的顺序依次进行计算。

快速幂与代替乘法的快速加法
快速幂实际上是快速幂取模,即求ab mod c的值
快速幂的思想,是把次数转化成二进制的形式进行快速计算,如计算a5,5(10)=101(2),把a5分解为a1 * a4进行计算。在计算中可以利用位运算来达成利用次数的二进制的效果。
代码如下:

long long quickpow(long long a,long long b)
{
    long long res=1;
        while(b>0)
        {
            if(b&1)res=(res*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
    return res;
}

在各种题目中,有时会出现两数相乘的结果超出long long,无法直接进行乘法计算的情况,此时需要使用快速加法代替乘法。
快速加法的思想和快速幂很像,同样是把乘数转化成二进制加以利用,如计算5a,就可以分解为1a+4a
需要注意的是,不是所有地方都需要用到快速加法代替乘法,若乘法结果不超出long long则可直接使用乘法,乱用该方法会有导致TLE的风险。
快速加法代码如下:

long long quickadd(long long a,long long b)
{
    long long res=0;
    while(b>0)
    {
        if(b&1)res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}

用快速加法代替乘法的快速幂:

long long quickpow(long long a,long long b)
{
    long long res=1;
    while(b>0)
    {
        if(b&1)res=quickadd(res,a);
        a=quickadd(a,a);
        b>>=1;
    }
    return res;
}

矩阵快速幂
矩阵快速幂适用于如下情况:
推导出答案的递推式,并且可以由几个项组成的n * 1矩阵,与n * n矩阵相乘,得到包含它们之后的一项,并且结构上与先前矩阵可以一一对应的矩阵。
例如:f ( n ) = f ( n - 1 ) + f ( n - 2 )
可推出该式子:
在这里插入图片描述
(求式子时往往先列出等号左边的矩阵和等号右边靠右的矩阵,再根据两矩阵内容填写剩余矩阵)

根据该式,可以通过对包含初值的矩阵反复相同矩阵得出包含最终答案的矩阵,又因矩阵运算的结合律,可以先求出矩阵的类乘积,然后再计算最终答案。
矩阵快速幂和数字的快速幂非常相似,在完成了矩阵运算符的重载后,可以说几乎是一模一样

斐波那契数列第n项的矩阵快速幂求解代码:

struct Matrix                      //构建矩阵结构体与运算符重载
{
    long long num[2][2];
    Matrix()
    {
        memset(num,0,sizeof(num));
    }
    Matrix operator*(const Matrix y)
    {
        Matrix ans;
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                for(int k=0;k<2;k++)
                    ans.num[i][j]+=num[i][k]*y.num[k][j];
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                ans.num[i][j]%=mod;
        return ans;
    } 
    void operator=(const Matrix b)
    {
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
                num[i][j]=b.num[i][j];
    }
}
int count(int x)
{
    Matrix ans,t;
    ans.num[0][0]=ans.num[1][1]=1;
    t.num[0][0]=t.num[0][1]=t.num[1][0]=1;
    while(x)
    {
        if(x&1)ans=ans*t;
        t=t*t;
        x>>=1;
    }
    return ans.num[0][0];
}

常用定理
这一部分以我的能力无法全部解释清楚了,只能花时间全部记下来,通过做题联系熟悉这些定理的用法

在这里插入图片描述

在这里插入图片描述
例题
HDU-2842 (矩阵快速幂)

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
struct Matrix
{
	long long num[3][3];
	Matrix()
	{
		memset(num,0,sizeof(num));
	}
	Matrix operator*(const Matrix y)
	{
		Matrix ans;
		for(int i=0;i<=2;i++)
			for(int j=0;j<=2;j++)
				for(int k=0;k<=2;k++)
					ans.num[i][j]+=num[i][k]*y.num[k][j];
		for(int i=0;i<=2;i++)
			for(int j=0;j<=2;j++)
				ans.num[i][j]%=200907;
		return ans;
	}
	void operator=(const Matrix b)
	{
		for(int i=0;i<=2;i++)
			for(int j=0;j<=2;j++)
				num[i][j]=b.num[i][j];
	}
};
int count(int x)
{
	Matrix ans,t;
	ans.num[0][0]=ans.num[1][1]=ans.num[2][2]=1;
	t.num[0][0]=1;
	t.num[0][1]=2;
	t.num[0][2]=1;
	t.num[1][0]=1;
	t.num[2][2]=1;
	while(x)
	{
		if(x&1)ans=ans*t;
		t=t*t;
		x>>=1;
	}
	return (ans.num[0][0]*2+ans.num[0][1]*1+ans.num[0][2]*1)%200907;
}
int main()
{
	int n;
	scanf("%d",&n);
	while(n!=0)
	{
		if(n==1)printf("1\n");
		else if(n==2)printf("2\n");
		else printf("%d\n",count(n-2));
		scanf("%d",&n);
	}
	return 0;
}

CodeForces - 1062C (前缀和+快速幂)

#include<cstdio>
#include<iostream>
#define mod 1000000007
using namespace std;
int num[100010];
long long quickpow(long long a,long long b)
{
	long long res=1;
	while(b>0)
	{
		if(b&1)res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res%mod;
}
int main()
{
	int n,q;
	long long ans;
	char s[100010];
	num[0]=0;
	scanf("%d%d",&n,&q);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
		num[i]=s[i]-'0'+num[i-1];
	while(q--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		int a=r-l+1,b=num[r]-num[l-1];
		a-=b;
		ans=((quickpow(2,b)-1)*quickpow(2,a))%mod;
		printf("%I64d\n",ans);
	}
	return 0;
}

HDU - 4135 (容斥原理)

#include<cstdio>
#include<iostream>
using namespace std;
int res[110];
struct node
{
	long long n;
	int flag;
}res1[100000],res2[100000];
int len,L;
void work(long long x,long long a,long long b)
{
	len=0;
	for(int i=2;1ll*i*i<=x;i++)
	{
		if(!(x%i))
			res[len++]=i;
		while(!(x%i))x/=i;
		if(x==1)break;
	}
	if(x>1)
		res[len++]=x;
}
int main()
{
	int t;
	long long a,b,n,ans;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		L=0;
		cin>>a>>b>>n;
		ans=b-a+1;
		printf("Case #%d: ",i);
		a--;
		work(n,a,b);
		for(int i=0;i<len;i++)
		{
			ans=ans-b/res[i]+a/res[i];
			int tmp=L;
			for(int j=0;j<tmp;j++)
			{
				res1[L].n=res1[j].n/res[i];
				res2[L].n=res2[j].n/res[i];
				res1[L].flag=res1[j].flag*(-1);
				res2[L].flag=res2[j].flag*(-1);
				ans=ans-res2[L].n*res2[L].flag+res1[L].n*res1[L].flag;
				L++;
			}
			for(int j=0;j<i;j++)
			{
				res1[L].n=1.0*a/(res[i]*res[j]);
				res2[L].n=1.0*b/(res[i]*res[j]);
				res1[L].flag=-1;
				res2[L].flag=-1;
				ans=ans-res2[L].n*res2[L].flag+res1[L].n*res1[L].flag;
				L++;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值