不知不觉,离CSP比赛只有两天了...
赶紧复习一波(尴尬的是,老师列出的复习内容分有好多我都没有系统学O.O)
复习计划:周四——数学相关;周五——数据结构相关
之前自己写了一篇总结:https://blog.csdn.net/qq_36294918/article/details/87552138
这里只做重点知识的回顾与强调,应该会有许多补充
先来谈谈【欧几里得算法】
个人觉得度娘的证明更让我能理解=。=:
//辗转相除法算最大公因数
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
//(x,y)*[x,y]=x*y——>lcm(x,y)=x*y/gcd(x,y)
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
【扩展欧几里得】
为了介绍扩展欧几里得,我们先介绍一下贝祖定理:
即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)
有一个直接的应用就是 如果ax+by=1有解,那么gcd(a,b)=1;
要求出这个最大公因数gcd(a,b),我们最容易想到的就是古老悠久而又相当强大的辗转相除法:
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}但是,对于上面的式子ax+by=m来说,我们并不仅仅想要知道有没有解,而是想要知道在有解的情况下这个解到底是多少。
所以,扩展欧几里得
当到达递归边界的时候,b==0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a*1+b*0=gcd(a,b),x=1,y=0,注意这时的a和b已经不是最开始的那个a和b了,所以我们如果想要求出解x和y,就要回到最开始的模样。
初步想法:由于是递归的算法,如果我们知道了这一层和上一层的关系,一层一层推下去,就可以推到最开始的。类似数学上的数学归纳法。
假设当前我们在求的时a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得 b*x1+(a%b)*y1=gcd
(注意在递归算法中,永远都是先得到下面一个状态的值)
这时我们可以试着去寻找这两个相邻状态的关系:
首先我们知道:a%b=a-(a/b)*b;带入:
b*x1 + (a-(a/b)*b)*y1
= b*x1 + a*y1 – (a/b)*b*y1
= a*y1 + b*(x1 – a/b*y1) = gcd 发现 x = y1 , y = x1 – a/b*y1
这样我们就得到了每两个相邻状态的x和y的转化,就可以在求gcd的同时对x和y进行求值了hiahia
参考博客:https://blog.csdn.net/destiny1507/article/details/81750874
int exgcd(int a,int b,int &x,int &y)//扩展欧几里得算法
{//ax+by=gcd(a,b)
if(b==0)
{
x=1;y=0;
return a;//到达递归边界开始向上一层返回
}
int r=exgcd(b,a%b,x,y);
//把x y变成上一层的
int temp=y;
y=x-(a/b)*y;
x=temp;
return r;//得到a,b的最大公因数
}
(大概看看吧,反正我也不懂=。=)
先谈谈【中国剩余定理】
•有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?——《孙子算经》•古代数学家给出了解决这个问题的口诀:三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五便得知•由于2是5*7=35关于3的逆元,1是3*7=21关于5的逆元,1是3*5关于7的逆元•“七十”=2*35,“廿一”=1*21,“正半月”=1*15,“百零五”=3*5*7•所以通解即为(2*70+3*21+2*15)+105k•最小的正整数解为23
(一)欧拉筛法:线性筛
一年前不理解,一年后的自己居然一看就看懂了qwq!
对每个合数a×b,它会被每个质因数都筛去一遍
但我们只要用最小的质因数筛去就好了
为此,我们需要记录下所产生的全部素数,代码如下
void sieve(int n)
{
for(int i=2;i<=n;i++)
{
if(!vis[i])
prime[++cnt]=i,vis[i]=1;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
【核心】
if(i%prime[j]==0) break;
如果i能整除primelist[k],
说明primelist[k]是i的因子,
所以primelist[k]也是i的任意倍数的因子。
所以primelist[k]也是i×primelist[x] (x>k)的因子。
考虑到primelist单增,对i×primelist[x],primelist[k]就是它的比primelist[x]更小的因子。
故不用考虑其后的质因子了。
(i×primelist[x]会被primelist[k]作为因子在i更大时被筛掉)
(二)欧拉筛法求约数个数d[n]
void sieve(int n)
{
d[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
prime[++cnt]=i,vis[i]=1,num[i]=1,d[i]=2;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
num[i*prime[j]]=num[i]+1;
d[i*prime[j]]=d[i]/(num[i]+1)*(num[i]+2);
break;
}
d[i*prime[j]]=d[i]*2;
num[i*prime[j]]=1;
}
}
}
(三)欧拉筛法求约数和s[n]
void sieve(int n)
{
s[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
prime[++cnt]=i,vis[i]=1,psum[i]=s[i]=i+1;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
psum[i*prime[j]]=psum[i]*prime[j]+1;
s[i*prime[j]]=s[i]/psum[i]*psum[i*prime[j]];
break;
}
s[i*prime[j]]=s[i]*(prime[j]+1);
psum[i*prime[j]]=1+prime[j];
}
}
}
• 积性函数对于所有互质的整数a和b有性质f ( a · b ) = f ( a ) · f ( b )
其实自己不是很理解积性函数
敲黑板!!!下面内容十分重要!
本人垃圾的小学奥数功底:约数个数口诀:指数加一再相乘
对于“因数和定理”,我个人的理解是:
对于第k种因数,可以为pk^0,pk^1,pk^2.....pk^ak,每种因数都是这样,最后根据【乘法原理】全部乘起来就是总和了
这里有个神仙博客!非常详细:https://www.cnblogs.com/Judge/p/9383034.html#_lab2_1_0
(一)费马小定理求逆元
% (用此方法时,p一定要是质数!)
int quick_pow(int x,int p)
{
int res=1;
while(p)
{
if(p&1)
res=(res*x)%mod;
x=(x*x)%mod,p>>=1;
}
return res;
}
int get_inv(int a)
{
return quick_pow(a,mod-2);
}
(二)exgcd求逆元
#include
#include
#include
#include
#include
using namespace std;
int exgcd(int a,int b,int &x,int &y)//ax+by=c
{
if(b==0)
{
x=1,y=0;
return a;
}
int r=exgcd(b,a%b,x,y);
int tmp=y;
y=x-(a/b)*y;
x=tmp;
return r;
}
int inv(int a,int p)
{
int d,x,y;
d=exgcd(a,p,x,y);
return d==1?(x+p)%p:-1;
}
int main()
{
int a,p;
scanf("%d%d",&a,&p);
printf("%d\n",inv(a,p));
return 0;
}
(三)递推法求逆元
//线性递推求逆元
void get_inv1()
{
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
//线性逆元求[阶乘逆元]法一
void get_inv2()
{
finv[0]=finv[1]=1;
for(int i=2;i<=n;i++)
finv[i]=finv[mod%i]*(mod-mod/i)%mod;
for(int i=2;i<=n;i++)
finv[i]=finv[i]*finv[i-1]%mod;
}
//线性逆元求[阶乘逆元]法二
void get_inv3()
{
fac[1]=finv[0]=1;
for(int i=2;i<=n;i++)
fac[i]=fac[i-1]*i%mod;
finv[n]=quick_pow(fac[n],mod-2);
for(int i=n-1;i>=1;i--)
finv[i]=finv[i+1]*(i+1)%mod;
}
【总结求逆元的多种方法】
1、当a和p互质时,逆元求解一般利用扩展欧几里得算法
2、当p为质数的时候直接使用费马小定理,p为非质数使用欧拉函数
3、当p为质数的时候,也可使用线性求[1,p-1]所有数逆元的方法
先来谈谈【欧拉函数】
int phi(int n)
{
int res=n;
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
//n*(1-1/pi)=n-n/pi=(n*pi-n)/pi=n*(pi-1)/pi
res=res/i*(i-1);
while(n%i==0)
n/=i;
}
if(n>1)
res=res/n*(n-1);
return res;
}
void sieve(int n)
{
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
prime[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
vis[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]*(prime[j]-1);
}
}
}
【欧拉定理】
不知道可以怎么用...降幂?
【补】指数循环节(似乎也可以叫“降幂公式”)
又一重点(黑板快敲烂了qwq...) 这个降幂公式蛮有用的
如其名字,就是用来降幂的,做题常用
不懂=。=,先咕掉
找到几篇神仙博客:
https://www.cnblogs.com/fzl194/p/9095177.html
https://blog.csdn.net/u011815404/article/details/81433925
https://blog.csdn.net/lmhacm/article/details/75929159
自己常用的方法:
(1)组合公式+快速幂求逆元
要求:P是质数
可根据上述公式,用快速幂算出m!( n-m )!的逆元(或分开求),再用 n ! * inv [ m ! * ( n - m ) ! ](数据较大的话记得中途取模)
LL powMod(LL x, LL n, LL mod)
{//快速幂求x^n%mod
LL res=1;
while(n)
{
if(n&1)
res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
LL inv(LL x,LL mod)
{//求逆元
return powMod(x,mod-2,mod);
}
LL fac[N];
int main()
{
LL n,m,mod;//要求mod是质数
scanf("%lld%lld%lld",&n,&m,&mod);
fac[0]=1;
for(int i=1;i<=n;i++)//预处理求fac,fac[i]=i!%mod
fac[i]=fac[i-1]*i%mod;
//C(n,m) = n!*(m!%mod的逆元)*((n-m)!%mod的逆元)%mod
LL res=fac[n]*inv(fac[m],mod)%mod*inv(fac[n-m],mod)%mod;
printf("%lld\n",res);
return 0;
}
此方法的例题我写了博客(nice):https://blog.csdn.net/qq_36294918/article/details/101561849
(2)杨辉三角打表(数据较小)
要求:n,m不超过10000
int Combination(int n)
{
int i,j;
a[0][0]=1;
for(i=0;i<=n;i++)
{
a[i][0]=a[i][i]=1;
for(j=1;j
(3)其他神仙方法有待学习... ...
https://www.cnblogs.com/fzl194/p/9095177.html
https://www.cnblogs.com/noobimp/p/10306311.html
自己以前做过一道博弈相关的小水题...看看这个找找感觉吧...
https://blog.csdn.net/qq_36294918/article/details/98368882
https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html
https://www.cnblogs.com/easonliu/p/4472541.html
警告——前方大量飞鸽!
啊,越复习感觉自己越菜...心里没啥把握...0.0...
加油加油再加油...