voidquick_sort(int q[], int l, int r) { if (l >= r) return; int i = l - 1, j = r + 1, x = q[l]; while (i < j) { do i ++ ; while (q[i] < x); do j -- ; while (q[j] > x); if (i < j) swap(q[i], q[j]); elsebreak; } quick_sort(q, l, j), quick_sort(q, j + 1, r); }
归并排序算法模板
voidmerge_sort(int q[], int l, int r) { if (l >= r) return; int mid = l + r >> 1; merge_sort(q, l, mid); merge_sort(q, mid + 1, r); int k = 0, i = l, j = mid + 1; while (i <= mid && j <= r) if (q[i] < q[j]) tmp[k ++ ] = q[i ++ ]; else tmp[k ++ ] = q[j ++ ]; while (i <= mid) tmp[k ++ ] = q[i ++ ]; while (j <= r) tmp[k ++ ] = q[j ++ ]; for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j]; }
intbsearch_1(int l, int r) { while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; // check()判断mid是否满足性质 else l = mid + 1; } return l; }
区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
intbsearch_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; }
浮点数二分算法模板
精度经验值 多2:
保留6位小数 r-l > 1e-8
保留4位小数 r-l > 1e-6
保留5位小数 r-l > 1e-7
boolcheck(double x){/* ... */} // 检查x是否满足某种性质
doublebsearch_3(double l, double r) { constdouble eps = 1e-6; // eps 表示精度,取决于题目对精度的要求 while (r - l > eps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; }
高精度加法
大整数存储
逆存,进位、高位补数更方便操作。
// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B) { if (A.size() < B.size()) returnadd(B, A); vector<int> C; int t = 0; for (int i = 0; i < A.size(); i ++ ) { t += A[i]; if (i < B.size()) t += B[i]; C.push_back(t % 10); t /= 10; } if (t) C.push_back(t); return C; }
高精度减法
(需转为 A>=B)
若有负数,则两数相减A-B,一定可以转换成|A|-|B|或|A|+|B|,分情况讨论。
// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B) { vector<int> C; for (int i = 0, t = 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; }
vector<int> mul(vector<int> &A, int b) { vector<int> C; int 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; } return C; }
vector<int> div(vector<int> &A, int b, int &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; }
// 二分求出x对应的离散化的值 intfind(int x) { int l = 0, r = alls.size() - 1; while (l < r) { int mid = l + r >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; } return r + 1; }
int st = -2e9, ed = -2e9; for (auto seg : segs) if (ed < seg.first) { if (st != -2e9) res.push_back({st, ed}); st = seg.first, ed = seg.second; } else ed = max(ed, seg.second);
voidquick_sort(int l,int r) { if(l >= r) return; ////补等于号 int x = q[(l+r)/2],i = l-1,j = r+1; //x应当固定q while(i < j) { do i++;while(q[i]<x); do j--;while(q[j]>x); if(i < j) swap(q[i],q[j]); } quick_sort(l,j); quick_sort(j+1,r); }
intmain() { scanf("%d", &n); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); quick_sort(0,n-1); for (int i = 0; i < n; i ++ ) printf("%d ",q[i]); return0; }
改进的快排
#include<bits/stdc++.h> usingnamespace std;
constint N = 1e6+10; int n;
voidswap(vector<int>& arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
vector<int> partiton(vector<int>& arr, int l, int r)//荷兰国旗问题 { int less = l - 1; int more = r; while (l<more) { if (arr[l] < arr[r]) { swap(arr, ++less, l++); } elseif (arr[l] > arr[r]) { swap(arr, --more, l); } else { l++; } } swap(arr, more, r); //默认以arr[r]做划分,不可遗漏最后一位数! return { less + 1,more }; }
voidquickSort(vector<int>& arr, int l, int r)//改进的快排,随机取数字做划分值 { if (l < r) { swap(arr, l + rand() % (r - l + 1), r); vector<int>p = partiton(arr, l, r); //返回划分区域(如[5 5 5])的左边界和右边界 quickSort(arr, l, p[0] - 1); //<区 quickSort(arr, p[1] + 1, r); //>区 } }
intmain() { scanf("%d", &n); vector<int> q(n); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); quickSort(q,0,n-1); for (int i = 0; i < n; i ++ ) printf("%d ",q[i]); return0; }
AcWing 786. 第k个数
输入
5 3 2 4 1 5 3
输出
3
解析
#include<bits/stdc++.h> usingnamespace std;
voidquick_sort(vector<int>&q,int l,int r) { while(l >= r) return; int x = q[l+(r-l>>1)],i = l-1,j =r+1; //注意(r-l>>1)括号位置。 while(i < j) { do ++i;while(q[i]<x); do --j;while(q[j]>x); if(i < j) swap(q[i],q[j]); } quick_sort(q,l,j); quick_sort(q,j+1,r);
}
intmain() { int n,k; scanf("%d%d", &n, &k); vector<int>q(n); for(int i = 0;i < n;++i) scanf("%d", &q[i]); quick_sort(q,0,n-1); printf("%d",q[k-1]); return0; }
降低复杂度。若左边数量已大于k,则只对左边进行排序;否则对右边。分治
#include<iostream> #include<cstring> #include<algorithm> // #include<bits/stdc++.h> usingnamespace std; constint N = 100010; int n,k; int q[N];
intquick_sort(int q[],int l,int r,int k) { if(l >= r) // if(l == r) return q[l]; int x = q[(l+r)>>1],i = l-1,j = r+1; while(i < j) { do ++i;while(q[i] < x); //while(q[++i] < x) do --j;while(q[j] > x); //while(q[--j] > x) if(i < j) swap(q[i],q[j]); } int sl = j - l + 1; if(k <= sl) quick_sort(q,l,j,k); elsereturnquick_sort(q,j+1,r,k-sl); }
intmain() { scanf("%d%d",&n,&k); for (int i = 0; i < n; i ++ ) scanf("%d",&q[i]); cout << quick_sort(q,0,n-1,k)<<endl; return0; }
归并排序
AcWing 787. 归并排序
输入
5 3 1 2 4 5
输出
1 2 3 4 5
解析
归并排序:
1.确定分界点
2.递归排序
3.归并————合二为一
#include<bits/stdc++.h> usingnamespace std; int n; int N = 100010; vector<int>tmp(N); vector<int>q(N); voidmerge_sort(vector<int>&q,int l,int r)//闭区间 { if(l >= r) return; int mid = l+(r-l>>1); //或 l+r>>1 merge_sort(q,l,mid); merge_sort(q,mid+1,r); int k = 0,i = l,j = mid+1; while(i <= mid && j <= r) { if(q[i] < q[j]) tmp[k++] = q[i++]; else tmp[k++] = q[j++]; } while(i <= mid) tmp[k++] = q[i++]; while(j <= r) tmp[k++] = q[j++]; for (int i = l,j = 0; i <= r; i ++,j++ ) q[i] = tmp[j]; //注意q[i]条件 } intmain() { scanf("%d", &n); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); merge_sort(q,0,n-1); for (int i = 0; i < n; i ++ ) printf("%d ",q[i]); }
vector<int> mul(vector<int>&A,vector<int>&B) { int len1 = A.size(),len2 = B.size(); vector<int>C(len1+len2); for(int i = 0;i < len1;++i) { for(int j = 0;j < len2;++j) { C[i+j] += A[i]*B[j]; } } int t = 0; for(int i = 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; }
intmain() { cin >> a >> b; vector<int>A,B; for(int i = a.size()-1;i >= 0;--i) A.push_back(a[i]-'0'); for(int i = b.size()-1;i >= 0;--i) B.push_back(b[i]- '0'); auto C = mul(A,B); for(int i = C.size()-1;i >= 0;--i) printf("%d",C[i]); return0; }
AcWing 794. 高精度除法
输入样例
7 2
输出样例
3 1
解析
区别前者,从最高位开始算。也可以不用从后往前存,此处为统一操作都从后向前存。
#include"bits/stdc++.h" usingnamespace std;
// A/b,商为C,余数是r vector<int> div(vector<int>&A,int &b,int &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; }
intmain() { string a; int b; cin >> a >> b; vector<int> A; for(int i = a.size()-1;i >= 0;--i) A.push_back(a[i] - '0'); int r = 0; auto C = div(A,b,r); for(int i = C.size()-1;i >=0;--i) printf("%d",C[i]); cout<<endl<<r<<endl; }
intmain() { scanf("%d", &n); for(int i = 0;i < n;++i) scanf("%d",&a[i]); int res = 0; for(int i = 0,j = 0;i < n;++i) { s[a[i]] ++; while(j < i && s[a[i]] > 1) //注意是 s[a[i]] > 1 i { s[a[j]] -- ; //注意是 s[a[j]]-- j j ++; } res = max(res,i-j+1); } cout<<res<<endl; return0; }
AcWing 800. 数组元素的目标和
给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。数组下标从 0 开始。求出满足 A[i]+B[j]=x 的数对 (i,j)。数据保证有唯一解。
输入样例
4 5 6 1 2 4 7 3 4 6 8 9
输出样例
1 1
解析
#include<bits/stdc++.h> usingnamespace std; constint N = 100010; int a[N],b[N]; int n,m,x; intmain() { cin >> n >> m >> x; for (int i = 0; i < n; i ++ ) scanf("%d",&a[i]); for (int i = 0; i < m; i ++ ) scanf("%d",&b[i]); for(int i = 0,j = m-1;i < n;++i) { while(j >= 0 && a[i]+b[j] > x) j--; if(j >= 0 && a[i] + b[j] == x) cout << i << ' ' << j << endl; } return0; }
双指针时间复杂度 O(n+m)。
如果题有多个答案,不能用双指针,复杂度为O(nm)。
AcWing 2816. 判断子序列
给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。判断 a 序列是否为 b 序列的子序列。子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5} 的一个子序列。
输入样例
3 5 1 3 5 1 2 3 4 5
输出样例
Yes
解析
#include<bits/stdc++.h> usingnamespace std; constint N = 100010; int a[N],b[N]; int n,m; intmain() { cin >> n >> m; for (int i = 0; i < n; i ++ ) scanf("%d",&a[i]); for (int i = 0; i < m; i ++ ) scanf("%d",&b[i]); int i = 0,j = 0; while(i < n && j < m) { if(a[i] == b[j]) i++; j++; } if(i == n) puts("Yes"); elseputs("No"); return0; }
位运算
AcWing 801. 二进制中1的个数
给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。
输入样例
5 1 2 3 4 5
输出样例
1 1 2 1 2
解析
1.求 n 第 k 位数字:n>>k&1
2.返回 n 最后一位1:lowbit(n) = n & -n
#include<bits/stdc++.h> usingnamespace std;
intlowbit(int &x) { return x&(-x); }
intmain() { int n; cin >> n; while (n -- ) { int x; cin >> x; int res = 0; while(x) x -= lowbit(x),res++; cout << res << ' '; } return0; }
voidmerge(vector<PII>&segs) { vector<PII>res; sort(segs.begin(),segs.end()); int st = -2e9,ed = -2e9; //起始维护值 for(auto seg:segs) { if(ed < seg.first) { if(st!=-2e9) res.push_back({st,ed}); st = seg.first, ed = seg.second; } else ed = max(ed,seg.second); } if(st!=-2e9) res.push_back({st,ed}); //加上最后一次 segs = res; }
intmain() { int n; cin >> n; for (int i = 0; i < n; i ++ ) { int l,r; cin >> l >> r; segs.push_back({l,r}); } merge(segs); cout << segs.size() <<endl; }