LCX的算法笔记Week-3
数学知识
给定一个正整数 n,请你求出 1∼n 中质数的个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> using namespace std; const int N=1e6+10; int n,cnt,primes[N]; bool state[N]; void getprimes(int n) { for (int i=2; i<=n; i++) { if (state[i]) { continue; } primes[cnt++]=i; for (int j=i+i; j<=n; j+=i) { state[j]=true; } } } int main() { cin>>n; getprimes(n); cout<<cnt; }
|
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
| #include <iostream> using namespace std; const int N=1e6+10; int n,cnt,primes[N]; bool state[N]; void getprimes(int n) { for (int i=2; i<=n; i++) { if (!state[i]) { primes[cnt++]=i; } for (int j=0; primes[j]<=n/i; j++) { state[primes[j]*i]=true; if (i%primes[j]==0) { break; } } } } int main() { cin>>n; getprimes(n); cout<<cnt; }
|
判断一个数是不是质数
1 2 3 4 5 6 7 8 9 10 11 12
| bool isprime(int n) { if (n==1) { return false; } for (int i=2; i<=n/i; i++) { if (n%i==0) { return false; } } return true; }
|
分解质因数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void divide(int n) { for (int i=2; i<=n/i; i++) { int cnt=0; while (n%i==0) { n/=i; cnt++; } if (cnt) { cout<<i<<' '<<cnt<<endl; } } if (n>1) { cout<<n<<' '<<1<<endl; } cout<<endl; }
|
试除法求一个数的所有约数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void func(int x) { vector<int> t; for (int i=1; i<=sqrt(x); i++) { if (x%i==0) { t.push_back(i); if (x/i!=i) { t.push_back(x/i); } } } sort(t.begin(),t.end()); for (int i=0; i<t.size(); i++) { cout<<t[i]<<' '; } cout<<endl; }
|
给定 n 个正整数 ai,请你输出这些数的乘积的约数个数,答案对 1e9+7 取模。
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; unordered_map<int, int> primes; int n; ll ans=1; int main() { cin>>n; while (n--) { int a; cin>>a; for (int i=2; i<=a/i; i++) { int res=0; while (a%i==0) { a/=i; res++; } primes[i]+=res; } if (a>1) { primes[a]++; } } for(auto i:primes) ans=ans*(i.second+1)%mod; cout<<ans; return 0; }
|

给定 n 个正整数 ai,请你输出这些数的乘积的约数之和,答案对 109+7 取模。
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; unordered_map<int, int> primes; int n; ll ans=1; int main() { cin>>n; while (n--) { int a; cin>>a; for (int i=2; i<=a/i; i++) { int res=0; while (a%i==0) { a/=i; res++; } primes[i]+=res; } if (a>1) { primes[a]++; } } ll ans=1; for(auto i:primes) { int base=i.first,p=i.second; ll res=1; while (p--) { res=(res*base+1)%mod; } ans=ans*res%mod; } cout<<ans; return 0; }
|
最大公约数
1 2 3 4
| int gcd(int a,int b) { return b?gcd(b,a%b):a; }
|
欧拉函数

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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; int n; int main() { cin>>n; while (n--) { int a; cin>>a; int ans=a; for (int i=2; i<=a/i; i++) { if (a%i==0) { ans=ans/i*(i-1); } while (a%i==0) { a/=i; } } if (a>1) { ans=ans/a*(a-1); } cout<<ans<<endl; } return 0; }
|
筛法求欧拉函数
给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7,N=1e6+10; ll n,ans; ll euler[N],prime[N],cnt; bool state[N]; void geteuler() { euler[1]=1; for (int i=2; i<=n; i++) { if (!state[i]) { prime[cnt++]=i; euler[i]=i-1; } for (int j=0; prime[j]<=n/i; j++) { int t=prime[j]*i; state[t]=true; if (i%prime[j]==0) { euler[t]=euler[i]*prime[j]; break; } euler[t]=euler[i]*(prime[j]-1); } } } int main() { cin>>n; geteuler(); for (int i=1; i<=n; i++) { ans+=euler[i]; } cout<<ans; return 0; }
|
扩展欧几里得算法
给定 n 对正整数 ai,bi,对于每对数,求出一组 xi,yi,使其满足 ai×xi+bi×yi=gcd(ai,bi)
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10,mod=1e9+7; int n; int exgcd(int a,int b,int & x,int & y) { if (!b) { x=1; y=0; return a; } int d=exgcd(b, a%b, y, x); y=y-a/b*x; return d; } int main() { cin>>n; while (n--) { int a,b,x,y; cin>>a>>b; exgcd(a, b, x, y); cout<<x<<' '<<y<<endl; } return 0; }
|
利用扩展欧几里得算法求线性同余方程
给定 n 组数据 ai,bi,mi,对于每组数求出一个 xi,使其满足 ai×xi≡bi(modmi),如果无解则输出 impossible
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10,mod=1e9+7; int n; int exgcd(int a,int b,int & x,int & y) { if (!b) { x=1; y=0; return a; } int d=exgcd(b, a%b, y, x); y=y-a/b*x; return d; } int main() { cin>>n; while (n--) { int a,x,y,m,b; cin>>a>>b>>m; int d=exgcd(a, m, x, y); if (b%d) { cout<<"impossible"<<endl; } else cout<<(ll)x*b/d%m<<endl; } return 0; }
|
扩展中国剩余定理
给定 2n 个整数 a1,a2,…,an 和 m1,m2,…,mn,求一个最小的非负整数 x
满足 ∀i∈[1,n],x≡mi(mod 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 38 39 40 41 42 43 44 45 46 47
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e4+10; int n; ll mod(ll x,ll m) { return ((x%m)+m)%m; } 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,m1; cin>>a1>>m1; bool flag=true; for (int i=1; i<n; i++) { int a2,m2; cin>>a2>>m2; ll k1,k2; ll d=exgcd(a1, a2, k1, k2); if ((m1-m2)%d) { flag=false; break; } k1*=(m2-m1)/d; k1=mod(k1, a2/d); m1=k1*a1+m1; a1=a1*a2/d; } ll ans=mod(m1, abs(a1)); if (flag) { cout<<ans; } else cout<<-1; return 0; }
|
高斯消元解线性方程组
输入一个包含 n 个方程 n 个未知数的线性方程组。方程组中的系数为实数。求解这个方程组。
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 72 73
| #include <bits/stdc++.h> using namespace std; const int N=1e3+10; const double eps=1e-6; int n; double a[N][N]; int gauss() { int r,c; for ( c=0,r=0; c<n; c++) { int t=r; for (int j=r; j<n; j++) { if (fabs(a[j][c])>fabs(a[t][c])) { t=j; } } if (fabs(a[t][c])<eps) { continue; } for (int j=c; j<n+1; j++) { swap(a[t][j],a[r][j]); } for (int j=n; j>=c; j--) { a[r][j]/=a[r][c]; } for (int j=r+1; j<n; j++) { if(fabs(a[j][c])>eps){ for (int k=n; k>=c; k--) { a[j][k]-=a[j][c]*a[r][k]; }} } r++; } if (r<n) { for (int j=r; j<n; j++) { if (fabs(a[j][n])>eps) { return 2; } } return 1; } for (int i=n-1; i>=0; i--) { for (int j=n-1; j>=i+1; j--) { a[i][n]-=a[i][j]*a[j][n]; } } return 0; } int main() { cin>>n; for (int i=0; i<n; i++) { for (int j=0; j<n+1; j++) { cin>>a[i][j]; } } int t=gauss(); if (t==0) { for (int i=0; i<n; i++) { cout<<fixed<<setprecision(2)<<a[i][n]<<endl; } } else if (t==1) { cout<<"Infinite group solutions"; } else cout<<"No solution"; return 0; }
|
当方程组为异或线形方程组时
yxc:

求组合数C(a,b)
从定义出发,时间复杂度O(n2)
1 2 3 4 5 6 7
| void init() { for (int i = 0; i < N; i ++ ) for (int j = 0; j <= i; j ++ ) if (!j) c[i][j] = 1; else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod; }
|
从用阶乘求组合数的定义出发,初始化阶乘,并且利用费马小定理和快速幂求逆元
时间复杂度O(kn),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
| ll fpow(ll base,ll p) { ll res=1; while (p) { if (p&1) { res=res*base%mod; } base=base*base%mod; p>>=1; } return res; } void init() { fac[0]=inv[0]=1; for (int i=1; i<N; i++) { fac[i]=fac[i-1]*i%mod; inv[i]=fpow((int)fac[i], mod-2); } } ll C(int a,int b) { return fac[a]*inv[b]%mod*inv[a-b]%mod; }
|
对于a,b都爆大的情况,利用lucas定理来求

1 2 3 4 5 6 7
| ll lucas(ll a,ll b) { if (a<p&&b<p) { return C(a, b); } return C(a%p, b%p)*lucas(a/p, b/p)%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 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 72
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; int primes[N],cnt,sum[N]; bool state[N]; vector<int> multi(vector<int> & a,int b) { vector<int> c; int t=0; 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; } void getprimes(int n) { for (int i=2; i<=n; i++) { if (!state[i]) { primes[cnt++]=i; } for (int j=0; primes[j]<=n/i; j++) { state[primes[j]*i]=true; if (i%primes[j]==0) { break; } } } } int getnum(int n,int p) { int res=0; while (n) { res+=n/p; n/=p; } return res; } int main() { int a,b; cin>>a>>b; getprimes(a); for (int i=0; i<cnt; i++) { int p=primes[i]; sum[i]=getnum(a,p)-getnum(b, p)-getnum(a-b, p); } vector<int> ans; ans.push_back(1); for (int i=0; i<cnt; i++) { for (int j=0; j<sum[i]; j++) { ans=multi(ans,primes[i]); } }
for (int i=ans.size()-1; i>=0; i--) { cout<<ans[i]; } return 0; }
|
卡特兰数
C(2n,n)-C(2n,n-1)=C(2n,n)/n+1
适用情况
- 括号匹配
- 出栈次序
- n个节点构成的二叉树,共有多少种情形
- 01序列-给定 n 个 0 和 n 个 1,它们将按照某种顺序排成长度为 2n 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 的个数都不少于 1 的个数的序列有多少个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int main() { int n; cin>>n; int a=2*n,b=n; ll res=1; for (int i=a; i>b; i--) { res=res*i%mod; } for (int i=1; i<=b; i++) { res=res*fpow(i, mod-2)%mod; } res=res*fpow(b+1, mod-2)%mod; cout<<res; return 0; }
|
容斥原理
给定一个整数 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
| #include <bits/stdc++.h> using namespace std; const int N=1e3+10; typedef long long ll; int p[17]; int main() { int n,m; cin>>n>>m; for (int i=0; i<m; i++) { cin>>p[i]; } ll ans=0; for (int i=1; i<1<<m; i++) { int cnt=0; ll res=1; int t=1; for (int j=0; j<m ; j++) { if (i>>j&1) { res*=p[j]; cnt++; if (res>n) { t=-1; break; } } } if (t!=-1) { if (cnt%2) { ans+=n/res; } else ans-=n/res; } } cout<<ans; }
|
博弈论
NIM游戏
给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
所有石子堆数量异或起来若为正,则一定有操作可以使之为0,最后输的状态就是0,因此只要开局时为正则必胜
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e4+10; int n; int main() { cin>>n; int res=0; while (n--) { int x; cin>>x; res^=x; } if (res) { cout<<"Yes"; } else cout<<"No"; return 0; }
|
台阶-Nim游戏
现在,有一个 n 级台阶的楼梯,每级台阶上都有若干个石子,其中第 i 级台阶上有 ai 个石子(i≥1)。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
和第一题类似,只要奇数台阶上石子数量异或起来为正就行啦

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e4+10; int n; int main() { cin>>n; int res=0; for (int i=1; i<=n; i++) { int x; cin>>x; if (i&1) { res^=x; } } if (res) { cout<<"Yes"; } else cout<<"No"; return 0; }
|
集合-Nim游戏(sg函数)
给定 n 堆石子以及一个由 k 个不同正整数构成的数字集合 S。
现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜

令终点sg函数值为0,递归求每个状态的sg值
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e4+10; int a[N],f[N],h[N]; int k,n; int sg(int x) { if (f[x]>0) { return f[x]; } unordered_set<int> s; for (int i=0; i<k; i++) { int t=a[i]; if (x>=t) { s.insert(sg(x-t)); } } for (int i=0; ; i++) { if (!s.count(i)) { return f[x]=i; } } } int main() { cin>>k; for (int i=0; i<k; i++) { cin>>a[i]; } cin>>n; int res=0; for (int i=0; i<n; i++) { cin>>h[i]; res^=sg(h[i]); } if (res) { cout<<"Yes"; } else cout<<"No"; return 0; }
|
拆分-Nim游戏
给定 n 堆石子,两位玩家轮流操作,每次操作可以取走其中的一堆石子,然后放入两堆规模更小的石子(新堆规模可以为 0,且两个新堆的石子总数可以大于取走的那堆石子数),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e4+10; int f[N]; int x,n; int sg(int x) { if (f[x]>=0) { return f[x]; } unordered_set<int> s; for (int i=0; i<x; i++) { for (int j=0; j<=i; j++) { s.insert(sg(i)^sg(j)); } } for (int i=0; ; i++) { if (!s.count(i)) { return f[x]=i; } } } int main() { memset(f, -1, sizeof f); int res=0; cin>>n; while (n--) { cin>>x; res^=sg(x); } if (res) { cout<<"Yes"; } else cout<<"No"; return 0; }
|