数论复习笔记
来源:互联网 发布:js 屏幕手写板 编辑:程序博客网 时间:2024/06/08 15:26
持续更新中……
1. 线性筛O(N)
基础的线性筛模板。
#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;const int maxn=1000005;int prime[maxn];bool notprime[maxn];int n,cnt=0;void shaiprime(){ for(int i=2;i<=n;i++) { if(!notprime[i]) prime[++cnt]=i; for(int j=1;j<=cnt;j++) { if(prime[j]*i>n) break; notprime[prime[j]*i]=true; if(i%prime[j]==0) break; //保证每个数只访问一次 } } }int main(){ scanf("%d",&n); notprime[1]=true; shaiprime(); for(int i=1;i<=cnt;i++) printf("%d ",prime[i]); return 0;}
2 . 线性筛求欧拉函数
欧拉函数用希腊字母φ表示,φ(N)表示N的欧拉函数。对φ(N)的值,我们可以通俗地理解为小于N且与N互质的数的个数(包含1)。欧拉函数的积性即:若m,n互质,则φ(mn)=φ(m)*φ(n);
欧拉函数的值:
设 n = p1^a1 ∗ p2^2 ∗ …… ∗ pk^ak
那么 ϕ(n) = n ∗ (1 − 1/p1 ) ∗ (1 − 2/p2 ) ∗ …… ∗ (1 − 1/pk )
phi【n】表示的是在[1,N]中与N互质的正整数个数,当一个数n是素数时,因为素数p除了1以外的因子只有p,所以与 p 互素的个数是 p - 1个,显然phi【n】=n-1;
关键在如下结论:
设P是素数, 若p是x的约数,则E(x*p)=E(x)p. 若p不是x的约数,则E(x*p)=E(x)*E(p)=E(x)(p-1).
证明:(来自http://blog.csdn.net/nk_test/article/details/46242401,只摘取部分)
E(x)表示比x小的且与x互质的正整数的个数。
* E(p^k)=p^k-p^(k-1)=(p-1)*P^(k-1)
证:令n=p^k,小于n的正整数数共有n-1即(p^k-1)个,其中与p不质的数共[p^(k-1)-1]个(分别为1*p,2*p,3*p…p(p^(k-1)-1))。所以E(p^k)=(p^k-1)-(p^(k-1)-1)=p^k-p^(k-1).得证。
所以
E(p^k) =(p-1)*p^(k-1)=(p-1)*p^(k-2)*p
E(p^(k-1))=(p-1)*p^(k-2)
->当k>1时,E(p^k)=E(p* p^(k-1))=E(p^(k-1))*p.
(当k=1时,E(p)=p-1.)
由上式: 设P是素数,
若p是x的约数,则E(x*p)=E(x) * p.
若p不是x的约数,则E(x* p)=E(x)* E(p)=E(x)*(p-1).
代码如下:
#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;const int maxn=200005;int prime[maxn],phi[maxn];bool notprime[maxn];int main(){ int n,cnt=0; scanf("%d",&n); notprime[1]=true; phi[1]=1; for(int i=2;i<=n;i++) { if(!notprime[i]) { prime[++cnt]=i; phi[i]=i-1; } for(int j=1;j<=cnt;j++) { if(prime[j]*i>n) break; notprime[prime[j]*i]=true; if(i%prime[j]==0) { phi[i*prime[j]]=prime[j]*phi[i]; break; } else phi[i*prime[j]]=(prime[j]-1)*phi[i]; } }}
3 . exgcd(扩展欧几里得)
即为求ax+by=gcd(a,b) 满足条件的额一组解(x,y)。这里给出中间式子的证明。
证明:
已知gcd(a,b)=gcd(b,a%b)
a%b=a-a/b*b .这里的a/b 默认为a/b下取整。
a * x+b* y=gcd(a,b)=gcd(b,a%b)
= b * x1 + (a - (a/b) * b) * y1
= b * x1 + a * y1–(a/b) * b * y1
= a * y1 + b * (x1–a/b * y1)
两式一一对应
∴ x = y1
y = x1 – a/b * y1
补充1:exgcd已知特解 x0,y0求通解通式
x=xo+b/gcd(a,b)*t;
y=yo-a/gcd(a,b)*t; 注:t为任意整数
证明:
相当于加了一个(a * b)/d,又在后面减了一个(a * b)/d。
已知ax0+by0=gcd(a,b)
则ax0+by0+a*b/gcd(a,b)*t-a*b/gcd(a,b)*t=gcd(a,b);
整理得 a(x0+b/gcd(a,b)*t)+b(yo-a/gcd(a,b)*t)=gcd(a,b);
一一对应
则x=x0+b/gcd(a,b)*t,y=y0+a/gcd(a,b)*t
因为t为任意整数,所以正负随意。
补充2:求最小正整数解:x=(x%b+b)%b。道理显然,这里不给出证明
代码如下:
#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;int exgcd(int a,int b,int &x,int &y){ if(b==0) { x=1,y=0; return a; } int d=exgcd(b,a%b,x,y); int tmp=x; x=y; y=tmp-a/b*y; return d;}int main(){ int a,b,x,y,d; scanf("%d%d",&a,&b); d=exgcd(a,b,x,y); cout<<d<<" "<<x<<" "<<y<<endl; return 0;}
4 .求解最小的同时满足n个模等式的值
法一:大数翻倍法
思路如下:
对于n个模等式
n%m1=a1
n%m2=a2
n%m3=a3
……
n%mk=ak
例如:如果有两个模等式,则首先保证n%m1=a1,然后
while(n%m2≠a2)
n+=m1(保证n%m1=a1依旧成立)
while(n%m3≠a3)
n+=lcm(m1,m2)(保证n%m1=a1,n%m2=a2依旧成立)
∴对于while(n%m2≠a2) n+=m1;
上式也可等同于 while(n%m1≠a1) n+=m2;
因此我们可以选择m1,m2中较小的来加(减少加的次数,取min)
复杂度大约为(m1+m2+……+mn)-max(m1,m2,……,mn)≈根号x
但是这个有个bug,就是对于较大的质数,如两个1e9+7,复杂度贼高,往往会T掉,一般的数据都是可行的。
代码如下:
#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;const int maxn=200005;int a[maxn],mo[maxn];int gcd(int a,int b){ if(b==0) return a; return gcd(b,a%b);}int lcm(int a,int b){ return a/gcd(a,b)*b;}int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&a[i],mo[i]); int ans=0,nowmo=1; for(int i=1;i<=n;i++) { int oth=a[i],othmo=mo[i]; if(othmo>nowmo) { swap(ans,oth); swap(nowmo,othmo); } while(ans%othmo!=oth) ans+=nowmo; nowmo=lcm(nowmo,othmo); } printf("%d\n",ans); return 0;}
5.容斥原理
小学数学,,,原理都懂,关键是看如何实现。这里是用一个类似队列来实现的。
给出的例题是求区间内与一个数互质的个数。
#include<iostream>#include<cmath>#include<cstdio>#include<cstdlib>using namespace std;const int maxn=200005;int q[maxn],jl[maxn];int a,b,n,tot=0;void divide(int n){ tot=0; for(int i=2;i*i<=n;i++) { if(n%i==0) { jl[++tot]=i; while(n%i==0) n/=i; } } if(n>1) //最后还剩下一个大质数 jl[++tot]=n;}int rc(int n) //利用容斥原理理的思想:去求区间[1,r]中不与n互素的个数。 { int sum=0,tmp=0; q[0]=-1; for(int i=1;i<=tot;i++) { int xx=tmp; for(int j=0;j<=xx;j++) q[++tmp]=q[j]*jl[i]*(-1); } for(int i=1;i<=tmp;i++) sum+=n/q[i]; return sum;}int main(){ int n,a,b,cnt=0; scanf("%d%d%d",&a,&b,&n); divide(n); printf("%d\n",(b-rc(b))-(a-1-rc(a-1))); return 0;}
6.逆元(a与m互质)
如果有,则把这个同余方程中的最小正整数解叫做模的逆元。逆元一般用扩展欧几里得算法来求得,如果为素数,那么还可以根据费马小定理得到逆元为 推导如下:
7.错位排序
就是所有的东西全被放错的可能出现的种数。证明过程(以下摘自百度百科):瑞士数学家欧拉按一般情况给出了一个递推公式:用A、B、C……表示写着n位友人名字的信封,a、b、c……表示n份相应的写好的信纸。把错装的总数为记作f(n)。假设把a错装进B里了,包含着这个错误的一切错装法分两类:
(1)b装入A里,这时每种错装的其余部分都与A、B、a、b无关,应有f(n-2)种错装法。
(2)b装入A、B之外的一个信封,这时的装信工作实际是把(除a之外的)(n-1 )份信纸b、c……装入(除B以外的)n-1个信封A、C……,显然这时装错的方法有f(n-1)种。
总之在a装入B的错误之下,共有错装法f(n-2)+f(n-1)种。a装入C,装入D……的n-2种错误之下,同样都有f(n-2)+f(n-1)种错装法,因此:f(n)=(n-1) {f(n-1)+f(n-2)}
公式可重新写成 f(n)-nf(n-1)=-[f(n-1)-(n-1)f(n-2)] (n>2)
于是可以得到
f(n)-nf(n-1)=-[f(n-1)-(n-1)f(n-2)]
=((-1)^2)[f(n-2)-(n-2)f(n-3)]
=((-1)^3)[f(n-3)-(n-3)f(n-4)]
=……
=[(-1)^(n-2)][f(2)-2f(1)]
最后可以整理为如下:f(n)=nf(n-1)+(-1)^(n-2)
f[1]=0,f[2]=1,f[3]=9,f[4]=44;
- 数论复习笔记
- noip数论复习总结
- 数论笔记
- 数论笔记
- 数论笔记
- 数论笔记
- 数论笔记
- 数论复习之扩欧
- 复习笔记
- 复习笔记
- NOIP2014复习之数论(一)
- 数论复习之中国剩余定理
- 简单数论知识梳理(省选复习)
- 数论学习笔记
- 数论概论学习笔记
- 数论学习笔记
- 数论学习笔记
- 数论学习笔记
- php+mysql+nginx
- 使用sqlalchemy包将pandas的DataFrame数据写入MySQL数据库
- 学习曲线
- 【程序员面试宝典】有1千万条短信,找出重复出现最多的前10条
- 神经网络训练细节(一)
- 数论复习笔记
- Oracle EBS财务模块(五)日记账
- mount nfs
- @ResponseBody注解使用返回类型为String时出现中文乱码
- Linux 服务器上面 使用 mutt+msmtp 进行定时邮件发送。
- VB字符串处理函数一览
- 区块宝周报:区块链一周大事排行榜10.09
- 每日区块链:区块链技术获得各国政府与顶级企业青睐;福布斯:招聘趋势——区块链技术和技术专家
- 将项目部署到tomcat