数论 质数判断(试除法) 复杂度为O(sqrt(n)) 若n%d==0, 则n%n/d==0 只需d<=n/d 即d*d<=n
1 2 3 4 5 6 7 8 9 10 11 12 bool checkprime (int n) { if (n<2 ){ return false ; } for (int i=2 ;i<=n/i;i++){ if (n%i==0 ){ return false ; } } return true ; }
分解质因数(试除法) 复杂度为O(logn)~O(sqrt(n))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void divide (int n) { if (n<2 ){ return ; } for (int i=2 ;i<=n/i;i++){ if (n%i==0 ){ int s=0 ; while (n/i==0 ){ n/=i; s++; } cout<<i<<" " <<s<<endl; } } if (n>1 ){ cout<<n<<" " <<1 <<endl; } pust ("" ); return ; }
筛质数 统计质数个数 ① 埃式筛法 唯一分解定理 任意合数可由素数p1^a1 p2^a2 p3^a3…pk^a3之积表示 (算术基本定理: 任何一个大于1的合数 N可以唯一分解成有限个质数的乘积) 1/2+1/3+…+1/n之和称为调和级数 当n->∞时 Σ->ln n+c (c称为欧拉常数≈0.57721) 时间复杂度为O(nloglogn)
1 2 3 4 5 6 7 8 9 10 void get_primes (int n) { for (int i=2 ;i<=n;i++){ if (!st[i]){ prime[cnt++]=i; for (int j=i+i;j<=n;j+=i){ st[j]=true ; } } } }
② 线性筛法 根据素数定理 从2到x的素数个数为x/ln x 若只遍历素数 时间复杂度为O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int cnt;int primes[N];bool st[N];void get_primes (int n) { for (int i=2 ;i<=n;i++){ if (!st[i]){ prime[cnt++]=i; } for (int j=0 ;primes[j]<n/i;j++){ st[primes[j]*i]=true ; if (i%primes[j]==0 ){ break ; } } } }
试除法求一个数的所有约数 1 2 3 4 5 6 7 8 9 10 11 12 13 vector<int > get_advisor (int n) { vector<int > a; for (int i=1 ;i<=n/i;i++){ if (n%i==0 ){ a.push_back (i); if (i!=n/i){ a.push_back (n/i); } } } sort (a.begin (),a.end ()); return a; }
约数个数 约数之和 约数个数定理:任何大于1的正整数n可以表示分解质因数n=Π(pi^ai) 则n的正约数个数为Π(ai+1) (pi^ai的约数有pi^1 pi^2…pi^ai共有ai+1个) INT范围内 最大的约数个数为1536个 n的约数之和为Π(pi^0+pi^1+…+pi^ai)可由乘法分配律展开证明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const int mod=1e9 +7 ;unordered_map<int ,int >primes; int main () { int n;cin>>n; while (n--){ int x;cin>>x; for (int i=2 ;i<=x/i;i++){ while (x%i==0 ){ x/=i; primes[i]++; } } if (x>1 ){ primes[x]++; } } LL res=1 ; for (auto prime:primes){ res=res*(prime.second+1 )%mod; } cout<<res<<endl; LL sum=1 ; for (auto prime:primes){ int p=prime.first,a=prime.second; LL t=1 ; while (a--){ t=(t*p+1 )%mod; } sum=(sum*t)%mod; } cout<<sum<<endl; return 0 ; }
欧几里得算法(辗转相除法) 证明: gcd(a,b)==gcd(b,a%b)=d; a=xd b=y d 即证gcd((xd)%(yd),yd)==d x y互质 故gcd(x%y,y)==1 同乘d 证毕
1 2 3 4 int gcd(int a,int b){ return b?gcd(b,a%b):a; //当b==0时 gcd(a,0)为a }
欧拉函数 1∼N 中与 N互质的数的个数被称为欧拉函数,记为 φ(N). 在算数基本定理中 N=Π(pi^αi)则 φ(N)=N*Π(1-1/pi)
1-1/pi=(pi-1)/pi 容质原理 ① 去掉p1、p2、p3…pn的所有倍数(可能一个数被去除了两次) ② 加上所有pi pj的倍数(任意两个) ② 减去所有pipj pk的倍数(任意三个) ··· ··· N-N/p1-N/p2…-N/pk +N/(pipj) i,j∈[1,n]&& i!=j -N/(pi pj*pk) i,j,k∈[1,n]&& i!=j&& i!=k&& j!=k ··· ··· 化简可得到欧拉函数的计算式 (由组合数公式可以确定各式的正负) 欧拉函数与质因子的次数无关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 typedef long long ll;ll phi (ll a) { ll res=a; for (int i=2 ;i<=a/i;i++){ if (a%i==0 ){ res=res*(i-1 )/i; } while (a%i==0 ){ a/=i; } } if (a>1 ){ res=res*(a-1 )/a; } return a; } int primes[N],st[N],phi[N];int cnt;void get_eulers (int n) { phi[1 ]=1 ; for (int i=2 ;i<=n/i;i++){ if (!st[i]){ primes[cnt++]=i; phi[i]=i-1 ; } for (int j=0 ;primes[j]<=n/i;j++){ st[primes[j]*i]==true ; if (i%primes[j]==0 ){ phi[primes[j]*i]=phi[i]*primes[j]; break ; } else { phi[primes[j]*i]=phi[i]*(primes[j]-1 ); } } } }
快速幂 给定 n组a,k,p 求a^k mod p的值
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef long long ll;ll qmi (ll a,ll k,ll p) { int res=1 ; while (k){ if (k&1 ){ res=(ll)res*a%p; } k>>=1 ; a=(ll)a*a%p; } return res; }
求逆元 (需满足p为质数) 设x为b的乘法逆元 a*x≡a/b(mod p) a与p互质
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 typedef long long ll;int qmi (ll a,ll k,ll p) { int res=1 ; while (k){ if (k&1 ){ res=(ll)res*a%p; } k>>=1 ; a=(ll)a*a%p; } } int main () { int n;cin>>n; ll a,p; while (n--){ cin>>a>>p; if (a%p){ cout<<qmi (a,p-2 ,p)<<endl; } else { puts ("impossible" ); } } return 0 ; }
裴蜀定理(拓展欧几里得实现) 对于任意正整数a,b 一定∃非0整数x, y s.t. ax+by=gcd(a,b) 根据拓展欧几里得定理 对给定a,b 求出满足条件的一组x和y gcd(a,b)=gcd(b,a%b) 当b==0时 gcd(a,0)==a 对ax+0 y==a a=1 b=0时成立 当b!=0时 gcd(b,a%b,y,x)==d by+(a%b)x=d && a%b==a-a/b a -> ax+b(y-a/bx)=d 故x不变 y更新为y-a/b x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int exgcd (int a,int b,int &x,int &y) { if (!b){ x=1 ,y=0 ; return a; } int d=gcd (b,a%b,y,x); y=y-a/b*x; return d; } int main () { int x;cin>>x; int a,b,x,y; while (n--){ cin>>a>>b; exgcd (a,b,x,y); cout<<x<<" " <<y<<endl; } return 0 ; }
找到ax0+b y0=d时 x=x0+b/dk y=y0+b/d k k∈Z 即为满足条件的通解
线性同余方程求解 给定a,b,m求 求是否∃x s.t. ax ≡ b(mod m) -> ax=ym+b 令y’=-y ->ax+ym=b ->解∃的条件为gcd(a,m)| b a是gcd(a,m)的倍数 m是gcd(a,m)的倍数 所以拓展a,m后的和一定也是gcd(a,m)的倍数 只有当gcd(a,m)| b时 才有解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int exgcd (int a,int b,int &x,int &y) { if (!b){ x=1 ,y=0 ;return b; } int d=gcd (b,a%b); y-=a/b*x; } int main () { int n;cin>>n; int a,b,m,x,y; while (n--){ cin>>a>>b>>b; int d=exgcd (a,m,x,y); if (b%d){ puts ("impossible" ); } else { cout<<(ll)x*(b/d)%m<<endl; } } }
中国剩余定理 对a1…an和m1…mn 求最小的非负整数x s.t. ∀i∈[1,n] x≡mi (mod ai) 记M为Πmi Mi为M/mi Mi^-1表示Mi模mi的逆 ∴x=Σ ai* Mi* Mi^-1 数据范围 1≤ai≤231−1 1≤n≤25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 typedef long long ll;ll exgcd (ll a,ll b,ll &x,ll &y) { if (!b){ x=1 ,y=0 ;return a; } ll d=exgcd (b,a%b,y,x); y-=a/b*x; return d; } int main () { int n;cin>>n; ll a1,a2,m1,m2; cin>>a1>>m1; bool has_answer=true ; for (int i=0 ;i<n-1 ;i++){ cin>>a2>>m2; ll k1,k2; ll d=exgcd (a1,a2,k1,k2); if ((m2-m1)%d){ has_answer=false ; break ; } k1*=(m2-m1)/d; ll t= a2/d; k1 = (k1%t+t)%t; m1 = k1*a1+m1; a1=abs (a1*a2/d); } if (has_answer){ cout<<(m1%a1+a1)%a1<<endl; } else { puts ("-1" ); } }
x=k1a1+m1 =(k1+k (a2/d))a1+m1 =a1k1+m1+k [a1,a2] a1与a2的最小公倍数 a1a2/d ∴m1 = k1 a1+m1; a1=abs(a1*a2/d);
高斯消元 解的判断 ① 完美阶梯型 唯一解 ② 0=bn (bn!=0)无解 ③ 0=0有无穷解
步骤 枚举每一列的 c ① 从第r列中 找到绝对值最大的一行 ② 将该行换到最上面 ③ 将该行第一个数变成1 ④ 将下面所有行的第c列消成0
组合计数 数学公式: C(b,a)=a!/(b!*(a-b)!); 证明: 0!=1; (n+1)!=(n+1)*n! 当n==0时 n!=1 证得
I 递推的方式: a,b∈[1,2000] C(b,a)=C(b,a-1)+C(b-1,a-1);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const int N=2010 ,mod=1e9 +7 ;void init () { for (int i=0 ;i<N;i++){ for (int j=0 ;j<n;j++){ if (!j){ a[i][j]=1 ; } else { a[i][j]=a[i-1 ][j]+a[i-1 ][j-1 ]; } } } } init ();cout<<c[a][b]<<endl;
II 预处理出阶乘和阶乘的逆元: a,b∈[1,1e5] n∈[1,1e5] 数学公式: C(b,a)=a!/(b!*(a-b)!);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 typedef long long ll;const int N=1e5 +10 ,mod=1e9 +7 ;int fact[N],infact[N];int qmi (ll a,ll k,ll p) { int res=1 ; while (k){ if (k&1 ){ res=(ll)res*a%p; } k>>=1 ; a=(ll)a*a%p; } return res; } int main () { fact[0 ]=infact[0 ]=1 ; for (int i=1 ;i<N;i++){ fact[i]=(ll)fact[i-1 ]*i%mod; infact[i]=(ll)infact[i-1 ]*qmi (i,mod-2 ,mod)%mod; } int n;cin>>n; while (n--){ ll a,b;cin>>a>>b; cout<<(ll)fact[a]*infact[b]%mod*infact[a-b]%mod<<endl; } }
lucas定理 给定 n 组询问,每组询问给定三个整数 a,b,p,其中 p 是质数,请你输出 C(b,a)mod p 的值。 a,b∈[1,1e18] p∈[1,1e5] a,b对p取模 C(b,a)=C(b%p,a%p)*C(b/p,a/p) (mod p)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 int p;int qmi (int a,int k) { int res=1 ; while (k){ if (k&1 ){ res=(ll)res*a%p; } k>>=1 ; a=(ll)a*a%p; } return res; } int C (int a,int b) { int res=1 ; for (int i=b,j=1 ;i>=1 ;i--,j++){ res=(ll)res*j%p; res=(ll)res*qmi (i,p-2 ,p)%p; } return res; } int lucas (ll a,ll b) { if (a<p&&b<p){ return C (a,b); } else { return (ll)C (a%p,b%p)+lucas (a/p,b/p); } } int main () { int n;cin>>n; while (n--){ ll a,b;cin>>a>>b; cout<<lucas (a,b)<<endl; } }
高精度乘法 (不求模) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 bool st[N];int primes[N];int cnt;void get_primes (int n) { for (int i=2 ;i<=n;i++){ if (!st[i]){ primes[cnt++]=i; } for (int j=0 ;primes[j]<=n/i;j++){ st[primes[j]*i]=true ; if (i%primes[j]==0 ){ break ; } } } } int sum[N];int get (int n,int p) { int res=0 ; while (n){ res+=rn/p; n/=p; } return res; } vector<int > mul (vector<int > a,int b) { vector<int > c; int t; for (int i=0 ;i<a.size ();i++){ t+=a[i]*b; c.push_back (t%10 ); t/=10 ; } while (t){ c.push_back (t%10 ); t/=10 ; } return c; } int main () { int a,int b; cin>>a>>b; get_primes (a); for (int i=0 ;i<cnt;i++){ int p=primes[i]; sum[i]=get (a,p)-get (b,p)-get (a-b,p); } vector<int >res; res.push_back (1 ); for (int i=0 ;i<cnt;i++){ for (int j=0 ;j<sum[i];j++){ res=mul (res,primes[i]); } } for (int i=res.size ()-1 ;i>=0 ;i--){ cout<<res[i]; } return 0 ; }
卡特兰数 给定 n 个 0 和 n 个, 它们将按照某种顺序排成长度为 2n 的序列, 求它们能排列成的所有序列中, 能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个.
输出的答案对 1e9+7 取模 1≤n≤10^5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 typedef long long ll;const int mod=1e9 +7 ,N=2e5 +10 ;int fact[N],infact[N];int qmi (int a,int k,int p) { int res=1 ; while (k){ if (k&1 ){ res=(ll)res*a%p; } k>>=1 ; a=(ll)a*a%p; } return res; } int main () { int n;cin>>n; fact[0 ]=infact[0 ]=1 ; for (int i=1 ;i<N;i++){ fact[i]=(ll)fact[i-1 ]*i%mod; infact[i]=(ll)infact[i-1 ]*qmi (i,mod-2 ,mod)%mod; } cout<<(ll)fact[2 *n]*infact[n]%mod*infact[n]%mod*qmi (n+1 ,mod-2 ,mod) %mod ; }
容斥原理 给定一个整数 n 和 m 个不同的质数 p1 p2 …pm 请你求出 1∼n 中能被 p1 p2 …pm 中的至少一个数整除的整数有多少个.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 typedef long long ll;const int N=20 ;int p[N];int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int n,m;cin>>n>>m; for (int i=0 ;i<m;i++)cin>>p[i]; int ans=0 ; for (int i=1 ;i<1 <<m;i++){ int t=1 ; int cnt=0 ; for (int j=0 ;j<m;j++){ if (i>>j&1 ){ cnt++; if ((ll)t*p[j]>n){ t=-1 ; break ; } t*=p[j]; } } if (t!=-1 ){ if (cnt%2 ){ ans+=n/t; } else { ans-=n/t; } } } cout<<ans<<endl; return 0 ; }
简单博弈论 Mex运算 设S表示一个非负整数集合.定义mex(S)为求出不属于集合S的最小非负整数运算,即: mes(S)=min{x}; 例如:S={0,1,2,4},那么mes(S)=3;
1 2 3 4 5 6 7 for (int i=0 ;;i++){ if (!S.count (i)){ f[x]=i; return f[x]; } }
SG函数 在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,····yk,定义SG(x)的后记节点y1,y2,···· yk的SG函数值构成的集合在执行mex运算的结果,即: SG(x)=mex({SG(y1),SG(y2)····SG(yk)}) 特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即 SG(G)=SG(s).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int f[M],s[N];int sg (int x) { if (f[x]!=-1 ) return f[x]; unordered_set<int > S; for (int i=0 ;i<m;i++){ int sum=s[i]; if (x>=sum) S.insert (sg (x-sum)); } for (int i=0 ;;i++) if (!S.count (i)) return f[x]=i; }