[关闭]
@LittleRewriter 2017-10-08T03:19:26.000000Z 字数 7317 阅读 914

数学

数学


模P意义下运算

加减乘除都一样

快速幂

  1. //求a^b%p
  2. //递归
  3. int ksm(int a,int b,int p){
  4. if(b==0) return l%p;
  5. int c=ksm(a,b>>1,p);
  6. c=c*1LL*c%p;
  7. if(b&1) c=c*a%p;
  8. return c;
  9. }
  10. //非递归
  11. int ksm(int a,int b,int p){
  12. int ret=l%p;
  13. while(b){
  14. if(b&1) ret=ret*a%p;
  15. b>>=1;
  16. a=a*a%p;
  17. }
  18. return ret;
  19. }

快速乘法

  1. int ksc(int a,int b,int p){
  2. int ret=0;
  3. while(b){
  4. if(b&1) ret=(ret+a)%p;
  5. b>>=1;
  6. a=(a+a)%p;
  7. }
  8. return ret;
  9. }

例题

NOIP2013 转圈游戏 P1965
n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏。按照顺时针方向给 n 个位置编号,从0 到 n-1。最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类推。游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类推,第n − m号位置上的小伙伴走到第 0 号位置,第n-m+1 号位置上的小伙伴走到第 1 号位置,……,第 n-1 号位置上的小伙伴顺时针走到第m-1 号位置。
现在,一共进行了 10^k轮,请问 x 号小伙伴最后走到了第几号位置。

快速幂裸题。

素数

判断

枚举法
Miller-Rabin定理

算数基本定理

筛法

O(nloglogn)筛

  1. bool vis[100000];
  2. for(int i=2;i<=n;++i){
  3. is(!vis[i]){
  4. for(int j=i;j*i<=n;++j)
  5. vis[j*i]=1;
  6. }
  7. }

比较好写,但是有重复
欧拉筛(O(n)筛)

  1. int mindiv[],prime[],tot;
  2. for(int i=2;i<=n;++i){
  3. if(!mindiv[i]) mindiv[i]=i,prime[tot++]=i;
  4. for(int j=0;prime[j]*i<=n;++j){
  5. mindiv[prime[j]*i]=prime[j];
  6. if(prime[j]==mindiv[i]) break;
  7. }
  8. }

保证被最小因子筛掉。
具体证明请见hzk大佬的Note吧...

gcd

gcd(a,b)=gcd(a,a-b)(更相减损术)

gcd(a,b)=gcd(b,a%b)(辗转相除法)

因此求gcd:

  1. int gcd(int a,int b){
  2. return b==0?a:gcd(b,a%b);
  3. }

可以预处理

lcm

与分解质因数的结合:

,
那么
由此也可以判断是否整除.

费马小定理


如果p不是素数请使用欧拉定理
可以用来算逆元
//以下所有运算都在取模意义下

因此逆元为
要求很多需要预处理
首先预处理出一个f数组,表示n!的逆元
那么有这样的关系:,即
而对于任意一个数n,有这样的性质:

由此可以O(n)的处理出逆元。
这个东西的证明:
https://margatroid.xyz/2017-10-04-Wilson-theorem/
一个神犇的blog
实现:

  1. int f[],inv[];
  2. f[0]=1;
  3. for(int i=1;i<=n;++i) f[i]=f[i-1]*i%p;
  4. int t=ksm(f[n],p-2,p);//快速幂,也可以exgcd
  5. for(int i=n;i>=1;--i){
  6. inv[i]=t*f[i-1]%p;
  7. t=t*i%p;
  8. }

exgcd

//这里没有听懂老师讲的...所以接下来的内容可能并不是笔记

裴蜀定理

若a,b 是整数, 且(a,b)=d,那么对于任意的整数x,y,ax+by 都一定是d
的倍数,特别地,一定存在整数x,y,使ax+by=d 成立。

exgcd

EXgcd用于解决这样一类问题:
已知(a,b),求解一组(p,q),使得pa+qb=gcd(a,b).
解是一定存在的,根据是数论中的某个相关定理。
那么怎么求呢?
pa+qb=gcd(a,b)=gcd(b,a%b)=pb+q(a%b)=pb+q(a-a/b*b)=qa+(p-a/b*q)b

显然p、q都是单调递减的。而我们在无数次的gcd过程中,b的值最终变成0,即p*a=a,显然此时p=1,q=0是一组解。那么我们有没有办法逆推回去?答案是肯定的。
接下来的代码,就是递归的求解一组x,y

  1. int exgcd(int a,int b,int &x,int &y){
  2. int t;
  3. if(!b) {x=1;y=0;return a;}
  4. t=exgcd(b,a%b,y,x);
  5. y-=(a/b)*x;
  6. return t;
  7. }

解不定方程和线性同余方程

这几条定律是我们解不定方程的依据。
而方程axc(mod b)ax+by=c,直接上Exgcd就好辣

求逆元

  1. void exgcd(int a,int b,int c,int &x,int &y){
  2. if(a==0){
  3. x=0,y=c/b;
  4. return;
  5. }else{
  6. int tx,ty;
  7. exgcd(b%a,a,tx,ty);
  8. x=ty-(b/a)*tx;
  9. y=tx;
  10. return;
  11. }
  12. }

例题

POJ1061 青蛙的约会
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B 的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。

根据小学数学,我们轻而易举的列出来这么一个方程,t为时间,p为圈数:
(x+m·t)-(y+n·t)=p·L
显然用exgcd能求出来t,p。为了方便代,我们不妨先整理一下:
(n-m)t+pL=x-y
要求条件:(x-y)%gcd(n-m,L)=0
最小正整数解:t=(t%m+m%t,其中m=
标程:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. //by zrt
  6. //problem:
  7. using namespace std;
  8. typedef long long LL;
  9. const int inf(0x3f3f3f3f);
  10. const double eps(1e-9);
  11. LL x0,y0,m,n,L;
  12. void exgcd(LL a,LL b,LL& x,LL& y,LL& d){
  13. if(b){
  14. exgcd(b,a%b,x,y,d);
  15. x-=(a/b)*y;
  16. swap(x,y);
  17. }else{
  18. d=a;x=1;y=0;
  19. }
  20. }
  21. int main(){
  22. #ifdef LOCAL
  23. freopen("in.txt","r",stdin);
  24. freopen("out.txt","w",stdout);
  25. #endif
  26. scanf("%lld%lld%lld%lld%lld",&x0,&y0,&m,&n,&L);
  27. LL x,y,a,b,c;
  28. a=m-n;
  29. b=L;
  30. c=y0-x0;
  31. LL gcd;
  32. exgcd(a,b,x,y,gcd);
  33. if(c%gcd){
  34. puts("Impossible");
  35. goto ed;
  36. }else{
  37. x=c/gcd*x;
  38. x=(x%L+L)%L;
  39. printf("%lld\n",x);
  40. }
  41. ed:return 0;
  42. }

中国剩余定理

公式法


那么

 

拉格朗日插值法

用exgcd两两合并

首先提取前两个式子,那么

相减,
解出来一组求出来x
可以形成一个新的式子

这里并不需要互素。

POJ 1006 Biorhythms
人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这一天,人在对应的方面(体力,情感或智力)表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。

列出三个类似(k-x)%23=0的式子。
联立即可。
标程:

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. //by zrt
  5. //problem:
  6. using namespace std;
  7. const int inf=(1<<30);
  8. typedef long long LL;
  9. const double eps=1e-8;
  10. int M=21252;
  11. void exgcd(LL a,LL b,LL&d,LL&x,LL&y){
  12. if(!b) d=a,x=1,y=0;
  13. else{
  14. exgcd(b,a%b,d,y,x);y-=(a/b)*x;
  15. }
  16. }
  17. LL R1,R2,R3;
  18. int p,e,i,D;
  19. int main(){
  20. #ifdef LOCAL
  21. freopen("in.txt","r",stdin);
  22. freopen("out.txt","w",stdout);
  23. #endif
  24. LL y,d;
  25. exgcd(M/23,23,d,R1,y);
  26. exgcd(M/28,28,d,R2,y);
  27. exgcd(M/33,33,d,R3,y);
  28. R1=(M/23*R1)%M;
  29. R2=(M/28*R2)%M;
  30. R3=(M/33*R3)%M;
  31. int tt=0;
  32. while(scanf("%d%d%d%d",&p,&e,&i,&D),~p){
  33. printf("Case %d: the next triple peak occurs in ",++tt);
  34. LL ans=(R1*p+R2*e+R3*i-D)%M;
  35. ans=(ans+M)%M;
  36. if(ans==0){
  37. printf("%d days.\n",M);
  38. }else printf("%lld days.\n",ans);
  39. }
  40. return 0;
  41. }

欧拉定理

剩余系与简系

剩余系:i的剩余系是1~i-1
简系:i的简系是剩余系中与p互素的部分

欧拉定理



欧拉函数的筛法

这一性质称为积性
,因此可以线性筛出欧拉函数

  1. void getphi(){
  2. int i,j;
  3. phi[1]=1;
  4. for(i=2;i<=40000;++i){
  5. if(!notprime[i]){
  6. prime[++cnt]=i;
  7. phi[i]=i-1;
  8. }
  9. for(j=1;j<=cnt;++j){
  10. if(i*prime[j]>0) break;
  11. notprime[i*prime[j]]=1;
  12. if(i%prime[j]==0){
  13. phi[i*prime[j]]=phi[i]*prime[j];
  14. break;
  15. }else{
  16. phi[i*prime[j]]=phi[i]*(prime[j]-1);
  17. }
  18. }
  19. }
  20. }

经典数学问题

余数之和

原式=
不妨回想一下我们上午的那道题。这个东西其实可以分成段,并且某一段区间这个值是相同的。
比如说,在[L,R],=k,那么这一段区间的余数之和就是,这个值可以用等差数列的公式搞一下。
而想要求这个东西也比较简单,R=X/L,这是因为这一段区间值其实是确定的。

Pick定理


一个格点多边形面积为内部格点数量+边上格点数量的一半-1
证明可以分割归纳。
QQ图片20171005184840.png

P2954
问内部的格点数量

海伦公式或者余弦定理求面积
关键是求边上的点
如果说长为a,宽为b,那么

这是因为每次的增长其实是在一条直线上,而gcd(a,b)其实是第一个交点的出现的位置。因此我们其实是求最小化的东西。
因此每边上有个整点。

矩阵乘法与矩阵快速幂

  1. for(int i=1;i<=n;++i)
  2. for(int j=1;j<=n;++j)
  3. for(int k=1;k<=n;++k)
  4. c[i][j]+=a[i][k]*b[k][j];

由此可以快速求一个递推式

矩阵快速幂=矩阵乘法+快速幂

例 斐波那契数列快速求值

对斐波那契数列,


ksm即可
标程:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. //by zrt
  6. //problem:
  7. using namespace std;
  8. typedef long long LL;
  9. const int inf(0x3f3f3f3f);
  10. const double eps(1e-9);
  11. LL x0,y0,m,n,L;
  12. void exgcd(LL a,LL b,LL& x,LL& y,LL& d){
  13. if(b){
  14. exgcd(b,a%b,x,y,d);
  15. x-=(a/b)*y;
  16. swap(x,y);
  17. }else{
  18. d=a;x=1;y=0;
  19. }
  20. }
  21. int main(){
  22. #ifdef LOCAL
  23. freopen("in.txt","r",stdin);
  24. freopen("out.txt","w",stdout);
  25. #endif
  26. scanf("%lld%lld%lld%lld%lld",&x0,&y0,&m,&n,&L);
  27. LL x,y,a,b,c;
  28. a=m-n;
  29. b=L;
  30. c=y0-x0;
  31. LL gcd;
  32. exgcd(a,b,x,y,gcd);
  33. if(c%gcd){
  34. puts("Impossible");
  35. goto ed;
  36. }else{
  37. x=c/gcd*x;
  38. x=(x%L+L)%L;
  39. printf("%lld\n",x);
  40. }
  41. ed:return 0;
  42. }

分治乘法


例如,
对于,(a + b)(c + d) = ac + ad + bc + bd
然后求出ad+bc可以求得结果
这样分治一下可以快一点

组合数

《组合数学》


求法:
C[n][m]=C[n-1][m]+C[n-1][m-1]
Lucas定理 C[n][m]%p=C[n%p][m%p]*C[n/p][m/p]
因此C[abcde][fghij]=C[a][f]*C[b][g]*C[c][h]*C[d][i]*C[e][j]

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注