voidquickSort(int a[], int l ,int r){ if (l >= r) return; int mid = a[(l + r) / 2], i = l - 1, j = r + 1; // i - 1 和 j + 1 都是为了处理边界问题 while (i < j) { do i ++; while (a[i] < mid); do j --; while (a[j] > mid); // 不需要判定相等的情况 if (i < j) swap(a[i], a[j]); } quickSort (a, l, j); quickSort (a, j + 1, r); }
intmain(){ scanf("%d", &n); for (int i = 0; i < n; i ++) scanf("%d", &a[i]); quickSort(a, 0, n - 1); for (int i = 0 ;i < n; i ++) printf("%d ", a[i]); return0; }
voidmergeSort(int q[], int l, int r){ if (l >= r) return; int mid = l + r >> 1; mergeSort (q, l, mid); mergeSort (q, mid + 1, r); int k = 0, i = l, j = mid + 1; while (i <= mid && j <= r) { if (q[i] <= q[j]) ans[k ++] = q[i ++]; else ans[k ++] = q[j ++]; } while (i <= mid) ans[k ++] = q[i ++]; while (j <= r) ans[k ++] = q[j ++]; for (int i = l, j = 0; i <= r; i ++, j ++) q[i] = ans[j]; }
intmain(){ scanf("%d", &n); for (int i = 0; i < n; i ++) scanf("%d" ,&q[i]); mergeSort(q, 0, n - 1); for (int i = 0; i < n; i ++) printf("%d", q[i]); return0; }
intbinarySearch(int l, int r){ while (l < r) { int mid = l + r + 1 >> 1; if (check(mid)) l = mid; else r = mid - 1; } return l; }
应用:查找大于等于 $x$ 的最小值
1 2 3 4 5 6 7 8 9
intbinarySearch(int a[], int n, int x){ int l = 1, r = n, mid; while (l < r) { mid = l + r >> 1; if (a[mid] >= x) r = mid; else l = mid + 1; } return l; }
应用:查找小于等于 $x$ 的最大值
1 2 3 4 5 6 7 8 9 10
intbinarySearch(int a[], int n, int x){ int l = 1, r = n, mid; while (l < r) { mid = l + r + 1 >> 1; // 需要保证 l < mid if (a[mid] <= x) l = mid; else r = mid - 1; } return l; }
浮点数二分
1 2 3 4 5 6 7 8 9 10
intbinarySearch(double l, double r){ double eps = 1e-4; // 精度下 2 位, 如此时保留 2 位小数 while (r - l < rps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; }
另外, 浮点二分可以转化为整数之后进行二分求解
二分答案
1 2 3 4 5 6 7 8 9 10
intbinarySearch(int l, int r){ int ans; while (l <= r) { // attention! l <= r int mid = l + r > 1; if (check(mid)) ans = mid, l = mid + 1; else r = mid - 1; } return ans; }
intmain(){ cin >> n >> m; for (int i = 1; i <= n; i ++) cin >> ans[i]; for (int i = 1; i <= n; i ++) ans[i] += ans[i - 1]; // 前缀和初始化 while (m --) { input(a), input(b); output(ans[b] - ans[a - 1]); } // 输出前缀和 return0; }
intmain(){ cin >> n; for (int i = 0; i < n; i ++) cin >> a[i]; int res = 0; for (int i = 0, j = 0; i < n; i ++) { s[a[i]] ++; while (s[a[i]] > 1) { s[a[j]] --; j ++; } res = max(res, i - j + 1); } cout << res << endl; return0; }
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; // 加 1 意为从数组下标的 1 处开始映射, 好处是可以方便前缀和的计算 }
int n, m, x, c; int a[N + 10]; // 离散化之后的序列 int s[N + 10]; // 离散化之后序列的前缀和 vector<int> alls; // 用于存储待离散化的值 vector<PII> add, access; // 对原序列进行的操作(加, 访问)
intfind(int x){ int l = 0, r = alls.size() - 1; while(l < r) { int mid = r + l >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; } return r + 1; // 从1开始将所有的数进行映射, 为了方便前缀和的计算 } // 找到x离散化后的结果 intmain(){ cin >> n >> m; for (int i = 0; i < n; i ++) { cin >> x >> c; add.push_back({x, c}); alls.push_back(x); } // 读入所有的插入操作 int l, r; for (int i = 0; i < m; i ++) { cin >> l >> r; access.push_back({l, r}); alls.push_back(l); alls.push_back(r); } // 读入所有的区间, 并求和 sort(alls.begin(), alls.end()); // 排序 alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去重 for (auto item : add) { int x = find(item.first); a[x] += item.second; } // 进行插入操作 for (int i = 1; i <= alls.size(); i ++) s[i] = s[i - 1] + a[i]; // 计算序列的前缀和 for (auto item : access) { int l = find(item.first), r = find(item.second); cout << s[r] - s[l - 1] << endl; } return0; }
高精度运算
若要直接使用封装好的高精度类型 BigNum, 请直接滑至文末.
基本思想
以字符串 / 字符数组存储大整数.
模拟人类计算过程按位计算.
重点和难点是判断进位以及借位的边界问题.
读入与存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
string a, b; vector<int> A, B; cin >> 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 = add(A, B); // 高精度加法 auto C = sub(A, B); // 高精度减法 auto C = mul(A, b) // 高精度乘法 auto C = div(A, b, t)// 高精度除法, t 为余数
for (int i = C.size() - 1; i >= 0; i --) printf("%d", C[i]); // 逆序输出
高精度加法 (add)
1 2 3 4 5 6 7 8 9 10 11 12 13
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 = t / 10; } if (t) C.push_back(1); return C; }
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); // 如果t > 0, 返回t; 如果t < 0, 返回t + 10; if (t < 0) t = 1; else t = 0; // 标记借位 } while (C.size() > 1and C.back() == 0) C.pop_back(); // 去掉前导0 return C; }
高精度减法输出需要特殊判定得数为负数的情况.
1 2 3 4 5 6 7 8 9 10 11
if (cmp(A, B)) { auto C = sub(A, B); for (int i = C.size() - 1; i >= 0; i --) printf("%d", C[i]); } else { auto C = sub(B, A); cout << '-'; for (int i = C.size() - 1; i >= 0; i --) printf("%d", C[i]); }
vector<int> mul(vector<int> &A, int b){ vector<int> C; int t = 0; // 表示当前数位上的值并处理进位问题 for (int i = 0; i < A.size() or 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(); // 处理多位数 * 0 导致 C 中出现多位0的情况 return C; }
vector<int> div(const vector<int> &A, int b, int &r){ // 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 = r % b; } reverse(C.begin(), C.end()); while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }