素数相关
(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;
}