算法笔记C++ | 总字数: 8.1k | 阅读时长: 37分钟 | 浏览量: |
算法笔记 算法基础 二分 整数二分 //在一个单调区间里面去找答案
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 bool check (int x) {} int bsearch_1 (int l, int r) { while (l < r) { int mid = l + r >> 1 ; if (check (mid)) r = mid; else l = mid + 1 ; } return l; } int bsearch_2 (int l, int r) { while (l < r) { int mid = l + r + 1 >> 1 ; if (check (mid)) l = mid; else r = mid - 1 ; } return l; } int bsearch_3 (int l,int r) { while (l <= r) { int mid = l + r >> 1 ; if (check (mid)) r = mid - 1 ; else l = mid + 1 ; } return l;} int lower_bound (vector<int >& nums, int target) { int left = -1 , right = nums.size (); while (left + 1 < right) { int mid = left + (right - left) / 2 ; if (nums[mid] >= target) { right = mid; } else { left = mid; } } return right; }
浮点数二分 1 2 3 4 5 6 7 8 9 10 11 12 bool check (double x) {} double bsearch_3 (double l, double r) { const double eps = 1e-6 ; while (r - l > eps) { double mid = l + r >> 2 ; if (check (mid)) r = mid; else l = mid; } return l; }
高精度 高精度比大小 1 2 3 4 5 6 7 8 9 bool cmp (vector<int > &A, vector<int > &B) { if (A.size () != B.size ())return A.size () >= B.size (); for (int i = A.size () - 1 ; i >= 0 ; i--) { if (A[i] != B[i])return A[i] >= B[i]; } return true ; }
高精度加法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> #include <vector> using namespace std;vector<int > add (vector<int > &A, vector<int > &B) { vector<int > C; int t=0 ; for (int i = 0 ; i < A.size () || i < B.size (); i++) { if (i<A.size ())t += A[i]; if (i<B.size ())t += B[i]; C.push_back (t % 10 ); t /= 10 ; } if (t)C.push_back (1 ); return C; }
高精度减法 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 bool cmp (vector<int > &A,vector<int > &B) { if (A.size ()!=B.size ()) return A.size ()>=B.size (); for (int i=A.size ()-1 ;i>=0 ;i--) { if (A[i]!=B[i])return A[i]>B[i]; } return true ; } vector<int > sub (vector<int > &A,vector<int > &B) { vector<int > C; int t=0 ; for (int i=0 ;i<A.size ();i++) { t=A[i]-t; if (i<B.size ())t-=B[i]; C.push_back ((t+10 )%10 ); if (t<0 )t=1 ; else t=0 ; } while (C.size ()>1 &&C.back ()==0 )C.pop_back (); return C; }
高精度乘法(高X低) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 vector<int > mul (vector<int >& A, long long b) { vector<int > C; long long t = 0 ; for (int i = 0 ; i < A.size () || t; i++) { if (i < A.size ()) t += A[i] * b; C.push_back (t % 10 ); t /= 10 ; } while (C.size () > 1 && C.back () == 0 ) C.pop_back (); return C; }
高精度乘法(高X高) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 vector<int > mul (vector<int > &A, vector<int > &B) { vector<int > C (A.size() + B.size()) ; for (int i = 0 ; i < A.size (); i++) { for (int j = 0 ; j < B.size (); j++) { C[i + j] += A[i] * B[j]; } } for (int i = 0 , t = 0 ; i < C.size (); i++) { t += C[i]; C[i] = t % 10 ; t /= 10 ; } while (C.size () > 1 && C.back () == 0 ) C.pop_back (); return C; }
高精度除法(高/低) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 vector<int > div (vector<int > &A, int b, long long &r) { vector<int > C; r = 0 ; for (int i = A.size () - 1 ; i >= 0 ; i--) { r = r * 10 + A[i]; C.push_back (r / b); r %= b; } reverse (C.begin (), C.end ()); while (C.size () > 1 && C.back () == 0 ) C.pop_back (); return C; }
前缀和 【算法笔记】前缀和与差分_前缀和差分-CSDN博客
一维前缀和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> using namespace std;const int N=100010 ; int a[N]; int main () { int n,m; cin>>n>>m; for (int i=1 ;i<=n;i++) { cin>>a[i]; a[i]=a[i-1 ]+a[i]; } while (m--){ int l,r; cin>>l>>r; cout<<a[r]-a[l-1 ]<<'\n' ; } return 0 ; }
二维前缀和
![[Pasted image 20250320190157.png]]
![[Pasted image 20250320190150.png]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int s[1010 ][1010 ];int n,m,q;int main () { scanf ("%d%d%d" ,&n,&m,&q); for (int i=1 ;i<=n;i++) for (int j=1 ;j<=m;j++) scanf ("%d" ,&s[i][j]); for (int i=1 ;i<=n;i++) for (int j=1 ;j<=m;j++) s[i][j]+=s[i-1 ][j]+s[i][j-1 ]-s[i-1 ][j-1 ]; while (q--){ int x1,y1,x2,y2; scanf ("%d%d%d%d" ,&x1,&y1,&x2,&y2); printf ("%d\n" ,s[x2][y2]-s[x2][y1-1 ]-s[x1-1 ][y2]+s[x1-1 ][y1-1 ]); } return 0 ; }
差分 一维差分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int a[100010 ],s[100010 ]; int main () { int n,m; cin>>n>>m; for (int i=1 ;i<=n;i++) { cin>>a[i]; s[i]=a[i]-a[i-1 ]; } while (m--){ int l,r,c; cin>>l>>r>>c; s[l]+=c; s[r+1 ]-=c; } for (int i=1 ;i<=n;i++){ s[i]+=s[i-1 ]; cout<<s[i]<<' ' ; } return 0 ; }
二维差分 二维差分用于在一个矩阵里,快速里把矩阵的一个子矩阵加上一个固定的数。也是直接来修改差分矩阵。试想只要在差分矩阵的( x 1 , y 1 ) 位置加上c,那么以它为左上角,所有后面的元素就都加上了c。要让( x 2 , y 2 ) 的右边和下边的元素不受影响,由容斥原理可以知道,只要在( x 2 + 1 , y 1 ) 和( x 1 , y 2 + 1 ) 位置减去c,再从( x 2 + 1 , y 2 + 1 ) 位置加回c就可以了。
![[Pasted image 20250320190138.png]]
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 const int N = 1e3 + 10 ; int a[N][N], b[N][N]; void insert (int x1, int y1, int x2, int y2, int c) { b[x1][y1] += c; b[x2 + 1 ][y1] -= c; b[x1][y2 + 1 ] -= c; b[x2 + 1 ][y2 + 1 ] += c; } int main () { int n, m, q; cin >> n >> m >> q; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) cin >> a[i][j]; for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= m; j++) { insert (i, j, i, j, a[i][j]); } } while (q--) { int x1, y1, x2, y2, c; cin >> x1 >> y1 >> x2 >> y2 >> c; insert (x1, y1, x2, y2, c); } for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= m; j++) { b[i][j] += b[i - 1 ][j] + b[i][j - 1 ] - b[i - 1 ][j - 1 ]; } } for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= m; j++) { printf ("%d " , b[i][j]); } printf ("\n" ); } return 0 ; }
动态规划DP LIS最长上升子序列 https://www.lanqiao.cn/problems/2049/learning/
朴素LIS O(n^2) 1 2 3 4 5 6 7 8 9 for (int i=1 ;i<=n;i++){ dp[i]=1 ; for (int j=1 ;j<i;j++) { if (a[i]>a[j])dp[i]=max (dp[j]+1 ,dp[i]); } ans=max (dp[i],ans); }
二分优化LIS O(nlogn)
LCS最长公共子序列 ![[Pasted image 20250320185940.png]]
1 2 3 4 5 6 7 8 9 10 11 for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=m;j++) { if (a[i]==b[j])dp[i][j]=dp[i-1 ][j-1 ]+1 ; else { dp[i][j]=max (dp[i-1 ][j],dp[i][j-1 ]); } } }
DFS 深搜就是从一个点一直搜到底再往上搜
例题:https://www.luogu.com.cn/problem/P1036
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 #include <iostream> using namespace std;int n, k, ans = 0 , a[30 ];bool sushu (int x) { if (x == 1 || x == 0 )return false ; else if (x == 2 )return true ; else { for (int i = 2 ; i * i <= x; i++) if (x % i == 0 )return false ; return true ; } } void dfs (int cnt, int sum, int t) { if (cnt == k) { if (sushu (sum))ans++; return ; } else { for (int i = t; i < n; i++) dfs (cnt + 1 , sum + a[i], i + 1 ); return ; } } int main () { cin >> n >> k; for (int i = 0 ; i < n; i++) cin >> a[i]; dfs (0 , 0 , 0 ); cout << ans; return 0 ; }
BFS 广搜就是一层到一层往下搜
例题:https://www.luogu.com.cn/problem/P1451
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 #include <iostream> #include <queue> using namespace std;int n,m,mv[4 ][2 ]={0 ,1 ,0 ,-1 ,1 ,0 ,-1 ,0 },ans;string mp[120 ]; void bfs (int x,int y) { queue<pair<int ,int > > q; q.push (make_pair (x,y)); while (!q.empty ()){ int x1=q.front ().first; int y1=q.front ().second; mp[x1][y1]='0' ; q.pop (); for (int i=0 ;i<4 ;i++){ int xi=mv[i][0 ]+x1,yi=y1+mv[i][1 ]; if (xi<0 ||yi<0 ||xi>=n||yi>=m)continue ; if (mp[xi][yi]!='0' ){ q.push (make_pair (xi,yi)); } } } } int main () { cin>>n>>m; for (int i=0 ;i<n;i++)cin>>mp[i]; for (int i=0 ;i<n;i++){ for (int j=0 ;j<m;j++){ if (mp[i][j]!='0' ){ bfs (i,j); ans++; } } } cout<<ans; return 0 ; }
数据结构 ST表 核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int n,q;int a[N];int mx[N][21 ],mi[N][21 ];void ST (int n) { int i,j; for (i=1 ;i<=n;i++) { mx[i][0 ]=a[i]; mi[i][0 ]=a[i]; } for (j=1 ;j<=21 ;j++) { for (i=1 ;(i+(1 <<j)-1 )<=n;i++) { mx[i][j]=max (mx[i][j-1 ],mx[i+(1 <<(j-1 ))][j-1 ]); mi[i][j]=min (mi[i][j-1 ],mi[i+(1 <<(j-1 ))][j-1 ]); } } }
求区间最大值
1 2 3 4 5 6 int maxquery (int l,int r) { int k=log2 (r-l+1 ); return max (mx[l][k],mx[r-(1 <<k)+1 ][k]); }
求区间最小值
1 2 3 4 5 int minquery (int l,int r) { int k=log2 (r-l+1 ); return min (mi[l][k],mi[r-(1 <<k)+1 ][k]); }
并查集 找根,判断连通
1 2 3 4 5 int root (int x) { return pre[x] = (pre[x]==x?x:root (pre[x])); }
启发式合并 平均查找O(1),避免树高度过高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void init () { for (int i=1 ;i<=n;++i)pre[i]=i,siz[i]=1 ; } int root (int x) { return pre[x]==x?x:pre[x]=root (pre[x]); } void merge (int x, int y) { int rx = root (x), ry = root (y); if (rx == ry)return ; if (siz[rx] > siz[ry])swap (rx, ry); pre[rx] = ry; siz[ry] += siz[rx]; }
单调栈 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 vector<int > next_greater (int n, const vector<int >& a) { vector<int > r (n, 0 ) ; stack<int > st; for (int i = 0 ; i < n; ++i) { while (st.size () && a[i] > a[st.top ()]) { r[st.top ()] = a[i]; st.pop (); } st.push (i); } return r; } vector<int > left_smaller (int n, const vector<int >& a) { vector<int > r (n, 0 ) ; stack<int > st; for (int i = 0 ; i < n; ++i) { while (st.size () && a[i] <= a[st.top ()] ) { st.pop (); } if (!st.empty ()) { r[i] = a[st.top ()]; } st.push (i); } return r; }
字符串 kmp 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 #include <iostream> #include <cstdio> #include <cstring> using namespace std;const int N = 1e6 + 10 ;int n, m, ne[N];char s[N],p[N];int main () { cin >> (p + 1 ); m = strlen (p + 1 ); cin >> (s + 1 ); n = strlen (s + 1 ); for (int i = 2 , j = 0 ; i <= m; ++i) { while (j && p[i] != p[j + 1 ]) j = ne[j]; if (p[i] == p[j + 1 ]) j++; ne[i] = j; } int ans = 0 ; for (int i = 1 , j = 0 ; i <= n; ++i) { while (j && s[i] != p[j + 1 ]) j = ne[j]; if (s[i] == p[j + 1 ]) j++; if (j == m) { ans++; j = ne[j]; } } cout << ans; return 0 ; }
Kruskal 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 struct Edge { int x, y, c; bool operator < (const Edge& u)const { return c < u.c; } }; int pre[10000 ];int root (int x) { return pre[x] == x ? x : root(pre[x]); }void solve () { int n, m; cin >> n >> m; vector<Edge> es; for (int i = 1 ; i <= m; ++i) { int x, y, c; cin >> x >> y >> c; es.push_back({ x, y, c }); } sort(es.begin(), es.end()); for (int i = 1 ; i <= n; ++i) pre[i] = i; int ans = 0 ; for (const Edge& e : es) { auto x = e.x; auto y = e.y; auto c = e.c; if (root(x) == root(y)) continue ; ans = max(ans, c); pre[root(x)] = root(y); } cout << ans << '\n' ; } int main () { solve(); return 0 ; }
Prim 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 struct Edge { long long x, c; bool operator < (const Edge& u)const { return c == u.c ? x > u.x : c > u.c; } }; vector<Edge> g[1000 ]; long long d[1000 ];int n, m;int prim () { priority_queue<Edge> pq; bool vis[10000 ]; d[1 ] = 0 ; pq.push ({ 1 , d[1 ] }); long long res = 0 ; while (pq.size ()) { int x = pq.top ().x; pq.pop (); if (vis[x]) continue ; vis[x] = true ; res = max (res, d[x]); for (const auto & elem : g[x]) { auto & y = elem.x; auto & w = elem.c; if (vis[y]) continue ; d[y] = min (d[y], w); pq.push ({ y, d[y] }); } } return res; } int main () { prim (); return 0 ; }
拓扑排序 有向无环图一定是拓扑序列
判断有无环的方法 :在进行拓扑排序时,使用入度数组记录每个顶点的入度(即指向该顶点的边的数量)。一开始,将所有入度为 0 的顶点入队列。然后从队列中取出顶点,减少其邻接顶点的入度,如果某个邻接顶点的入度变为 0,则将其入队列。重复这个过程,直到队列为空。如果拓扑排序结束后,图中还有顶点的入度不为 0,说明存在一些顶点无法被纳入拓扑排序中,这就意味着图中存在环;反之,如果所有顶点都被成功加入到拓扑排序中,那么图就是一个有向无环图。 例如,对于一个有向图,经过上述拓扑排序操作后,若存在未处理的顶点(入度不为 0),则可判定该图有环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 queue<int > q; vector<int > g[N]; for (int i=0 ;i<m;i++) { int u,v; cin>>u>>v; ind[v]++; g[u].push_back (v); } for (int i=1 ;i<=n;i++){ if (!ind[i]) q.push (i); } while (!q.empty ()){ int x=q.front (); q.pop (); for (auto y:g[x]) { ind[y]--; if (!ind[y])q.push (y); } }
拓扑序列判断无环 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 const int N = 100010 ;vector<int > g[N]; int ind[N];bool topologicalSort (int n) { queue<int > q; for (int i = 1 ; i <= n; i++) { if (!ind[i]) q.push (i); } int count = 0 ; while (!q.empty ()) { int x = q.front (); q.pop (); count++; for (auto y : g[x]) { ind[y]--; if (!ind[y]) q.push (y); } } return count == n; } int main () { int n, m; cin >> n >> m; for (int i = 0 ; i < m; i++) { int u, v; cin >> u >> v; ind[v]++; g[u].push_back (v); } if (topologicalSort (n)) { cout << "图中没有环" << endl; } else { cout << "图中存在环" << endl; } return 0 ; }
Floyd 多源最短路,求出每个点与点之间的最短路径
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 #include <iostream> typedef long long ll;const int N = 210 ; const ll INF = 1e18 ; ll d[N][N]; using namespace std;int main () { ios::sync_with_stdio (0 ), cin.tie (0 ), cout.tie (0 ); int n, m, q; cin >> n >> m >> q; for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= n; j++) { if (i == j) d[i][j] = 0 ; else d[i][j] = INF; } } while (m--) { ll u, v, w; cin >> u >> v >> w; d[u][v] = min (d[u][v], w); d[v][u] = min (d[v][u], w); } for (int k = 1 ; k <= n; k++) { for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= n; j++) { d[i][j] = min (d[i][j], d[i][k] + d[k][j]); } } } while (q--) { int st, ed; cin >> st >> ed; if (d[st][ed] > INF / 2 ) cout << "impossible" << endl; else cout << d[st][ed] << endl; } return 0 ; }
Dijkstra 求单源最短路,求出所有点距离源点的最短距离
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 74 75 76 77 78 79 80 81 82 83 84 85 #include <bits/stdc++.h> using namespace std;typedef long long ll;const int MAXN = 3e5 + 10 ;constexpr ll INF = 1E18 ;int n;int m;ll dist[MAXN]; priority_queue<pair<ll, int >, vector<pair<ll, int >>, greater<>> pq; vector<pair<int , ll>> graph[MAXN]; void dijkstra (int start) { pq.emplace (0LL , start); while (!pq.empty ()) { auto [d, u] = pq.top (); pq.pop (); if (dist[u] != INF) continue ; dist[u] = d; for (auto [v, w] : graph[u]) { pq.emplace (d + w, v); } } } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); cin >> n >> m; for (int i = 1 ; i <= n; i++) { dist[i] = INF; } for (int i = 1 ; i <= m; i++) { int u, v; ll w; cin >> u >> v >> w; graph[u].push_back ({v, w}); } dijkstra (1 ); for (int i = 1 ; i <= n; i++) { if (dist[i] == INF) { cout << -1 << ' ' ; } else { cout << dist[i] << ' ' ; } } return 0 ; }
数论 gcd 最大公因数,或称最大公约数
1 2 3 4 __gcd(int a,int b); int gcd (int a,int b) { return b == 0 ? a : gcd (b,a%b); }
lcm 最大公倍数
1 2 3 4 __lcm(int a,int b); int lcm (int a,int b) { return a/gcd (a,b)*b; }
素数 朴素素数 1 2 3 4 5 bool isprime (int n) { if (n<2 )return false ; for (int i=2 ;i<=n/i;i++)if (n%i==0 )return false ; return true ; }
埃氏筛法 比朴素快很多倍
1 2 3 4 5 bool vis[N];vis[0 ]=vis[1 ]=true ; for (int i=2 ;i<=n/i;i++){ if (!vis[i])for (int j=i*i;j<=n;j+=i)vis[j]=true ; }
欧拉筛 o(n)最快,可以在1s内筛出约1e7以内的所有质数,埃氏筛法能做的欧拉筛都能做。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void euler (int n) { vector<int > primes; vis[0 ] = vis[1 ] = true ; for (int i = 2 ;i <= n; ++i) { if (!vis[i])primes.push_back (i); for (int j = 0 ;j < primes.size () && i * primes[j] <= n; ++j) { vis[i * primes[j]] = true ; if (i % primes[j] == 0 ) { break ; } } } }
唯一分解定理 唯一分解定理指的是:任何一个大于1的自然数都可以唯一地分解为有限个质数的乘积。 $$ (x = p_1^{k_1}×p_2^{k_2}×…×p_m^{k_m}) $$ 这个式子中的p1,p2是类似2, 3, 5, 7这样的质数。 将单个数字进行质因数方法是,从小到大枚举x的所有可能的质因子,最大枚举到sqrt(x),每遇到一个可以整除的数字i,就不断进行除法直到除尽。最后如果还有x>1,说明还有一个较大的质因子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <bits/stdc++.h> using namespace std;const int N = 2e5 + 9 ;vector<pair<int , int >> v; int main () { int x;cin >> x; for (int i = 2 ;i <= x / i; ++ i) { if (x % i)continue ; int cnt = 0 ; while (x % i == 0 )cnt ++, x /= i; v.push_back ({i, cnt}); } if (x > 1 )v.push_back ({x, 1 }); for (const auto i : v)cout << i.first << ' ' << i.second << '\n' ; return 0 ; }
约数个数定理 通过某个数字的唯一分解: $$ x = p_1^{k_1}×p_2^{k_2}×…×p_m^{k_m} $$ 我们可以求出x的约数(因数)个数,如果学过线性代数或者有向量相关的知识的话,可以理解为将不同的质因子看作是不同的向量空间或基底,不同质因子之间互不干扰。 也就是说p1的指数的取值是[0, k1]共(k1 + 1)个,p2,p3…亦然,所以x的约数的个数就是 $$ (k1 + 1)(k2 + 1) …(km + 1) $$ ,即: $$ d(x)=\prod_{i = 1}^{m}(k_i + 1) $$ 例题:定义阶乘 n!=1×2×3×⋅⋅⋅×n n*!=1×2×3×⋅⋅⋅×n 。
请问 100!(100 的阶乘)有多少个正约数。
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 #include <iostream> using namespace std;#define int long long int num[105 ];void init (int n) { for (int i=2 ;i<=n/i;i++){ if (n%i)continue ; while (n%i==0 )num[i]++,n/=i; } if (n>1 )num[n]++; } signed main () { for (int i=1 ;i<=100 ;i++){ init (i); } int ans=1 ; for (int i=1 ;i<=100 ;i++){ ans*=(num[i]+1 ); } cout<<ans; return 0 ; }
约数和定理 通过某个数字的唯一分解: $$ x = p_{1}^{k_{1}}\times p_{2}^{k_{2}}\times… \times p_{m}^{k_{m}} $$ 我们可以求出x的约数(因数)之和,与约数个数定理类似。p1对于约数和的贡献为1或p1或p1^2或…或p1^k1,于是x的约数之和可以表达为: $$ sum(x) =\prod_{i = 1}^{m}\sum_{j = 0}^{k_{i}}p_{i}^{j} $$ 约数和计算公式: f[i] = (p1^0 + p1^1 + p1^2 + p1^3… + p1^c1) * (p2^0 + p2^1 + p2^2 + p2^3…p2^c2) * ……
例题:给定你一个正整数 nn ,你需要求出 n!n ! 的约数之和,结果对 998244353998244353 取模。
n!:n的阶乘,含义为 1×2×3×…×n1×2×3×…×n 。
输入包含一个正整数 nn 。
输出 n! 的约数之和,对 998244353998244353 取模。
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 #include <iostream> using namespace std;#define MOD 998244353; #define int long long int n;int num[200005 ];void init (int m) { for (int i=2 ;i<=m/i;++i){ if (m%i)continue ; while (m%i==0 )m/=i,num[i]++; } if (m>1 )num[m]++; } signed main () { cin>>n; for (int i=2 ;i<=n;i++){ init (i); } int ans=1 ; for (int i=2 ;i<=n;i++){ int sum=0 ,tmp=1 ; if (num[i]==0 )continue ; for (int j=0 ;j<=num[i];j++){ sum=(sum+tmp)%MOD; tmp=(tmp*i)%MOD; } ans=(ans*sum)%MOD; } cout<<ans; return 0 ; }
快速幂 朴素的计算a的b次方的方法,所需的时间复杂度为O(b),即用一个循环,每次乘一个a,乘b次。 利用倍增的思想,可以将求幂运算的时间复杂度降低为O(logb)。 当b为偶数: $$ a^b=a^(b/2)a^(b/2)=(a^2)^(b/2) $$ 当b为奇数: $$ a^b=a a^(b/2)a^(b/2)=a (a^2)^(b/2) $$ ,注意这里b/2向下取整。 于是迭代求解,直到b为0即可。
1 2 3 4 5 6 7 8 9 10 11 int qmi (int a, int b, int p) { int res = 1 ; while (b) { if (b&1 )res = res * a % p; a = a * a % p; b >>= 1 ; } return res%p; }