// 中点 voidquick_sort(int q[], int l, int r) { if(l>=r) return; int i = l - 1, j = r + 1, x = q[l + r >> 1]; // (l + r >> 1)使l+r的值右移1位,相当l+r的值除以2取整。 // i,j为左右两个端点的两侧的指针。注意之所以分别加减1是因为默认会先把指针向中间移动再进行比较,所以这里先移动到左右端点的两侧 while(i < j) // 一般循环结束时,i == j + 1 { 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); // 注意当写成quick_sort(q, l , i - 1),quick_sort(q, l, i),x不能取q[l](调用左指针时,不取左端点)例如样例为1,2时会陷入死循环 // 写成quick_sort(q,j + 1, r)时,x 不能取q[r](调用右指针时,不取右端点) }
// 左端点 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[i] > x); if (i < j) swap(q[i], q[j]); } quick_sort(q, l, j), quick_sort(q, j + 1, r); }
intbsearch_1(int l, int r, int k){ while (l < r) // 区间长度大于等于1 { // 分界点在右半部分 int mid = l + r >> 1; if (q[mid] >= k) r = mid; // 成立时,对右端点更新且包含mid(分界点在成立区间得左端) else l = mid + 1; } return l; // 最终l==r } intbsearch_2(int l, int r, int k){ while (l < r) { // 分界点在左半部分 int mid = l + r + 1 >> 1; // 当分界点在左半部分时,之所以$mid = \frac{l+r+1}{2}$多加一个1是为了避免mid = l导致更新时l = mid会不发生变化,陷入死循环(这种情况出现在l = r - 1时)。 if (q[mid] <= k) l = mid; // 成立时,对左端点更新且包含mid(分界点在成立区间得右端) else r = mid - 1; // 不成立时,对右端点更新,且不包含mid } return l; // 最终l==r }
intmain(){ scanf("%d%d", &n, &m); for (int i = 0; i < n; ++ i) scanf("%d", &q[i]); while (m--){ scanf("%d", &k); // 求k的起始位置,利用 x >= k的性质二分 int a = bsearch_1(0, n-1, k); // 注意判断是否存在的方法——如果边界值不等于k,说明数组中不存在k if (q[a] != k) puts("-1 -1"); else { int b = bsearch_2(0, n-1, k); // 求k的终止位置,利用 x <= k的性质二分 printf("%d %d\n", a, b); } } return0; }
# 整数二分——AcWing 789. 数的范围 defmain(): deflower_bound(l, r): while l < r: mid = l + r >> 1 if q[mid] >= k: r = mid else: l = mid + 1 return l defupper_bound(l ,r): while l < r: mid = l + r + 1 >> 1 if q[mid] <= k: l = mid else: r = mid - 1 return l n, m = map(int, input().split()) q = list(map(int, input().split())) for _ inrange(m): k = int(input()) l = lower_bound(0, n - 1) if q[l] != k: print("-1 -1") else: r = upper_bound(0, n - 1) print(l, r) if __name__ == "__main__": main()
当x存在多个时,bisect_left返回最左边的x的索引bisect_right返回最右边的x的索引加1;如果元素不存在,则返回将其插入到何处
eg: l = [1, 4, 5],bisect_left(l, 4)返回1,bisect_left(l, 2)返回1,bisect_left(l, 6)返回3
因为bisect返回大于x的最左的第一个下标,所以其减一即得到小于等于x的最右侧的数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# 整数二分——AcWing 789. 数的范围 from bisect import bisect, bisect_left defmain(): n, m = map(int, input().split()) q = list(map(int, input().split())) for _ inrange(m): k = int(input()) l = bisect_left(q, k) # 这里需要注意,如果元素不存在,则返回将其插入到何处,因而返回值可能越界(插在最后一个元素后面),需要特判 if l >= n or q[l] != k: print("-1 -1") else: r = bisect(q, k) - 1 print(l, r) if __name__ == "__main__": main()
// C = A + B, A >= 0, B >= 0 vector<int> add(vector<int> &A, vector<int> &B){ if (A.size() < B.size()) return add(B, A); // 位数多的放前面
vector<int> C;
int t = 0; // t用于表示进位
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; }
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; }
// C = A + B, A >= 0, B >= 0 vector<int> add(vector<int> &A, vector<int> &B){ if (A.size() < B.size()) return add(B, A); // 位数多的放前面
vector<int> C;
int t = 0; // t用于表示进位
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; }
intmain(){ string a, b; vector<int> A, B; cin >> a >> b; // 不建议使用scanf 输入string类型字符串 // 如果使用scanf,必须要为string提前分配足够空间 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); for (int i = C.size() - 1; i >= 0; -- i) printf("%d", C[i]); puts(""); return0; }
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 % base); t /= base; }
if (t) C.push_back(t); return C; }
intmain() { string a, b; vector<int> A, B; cin >> a >> b;
for (int i = a.size() - 1, s = 0, j = 0, t = 1; i >= 0; i -- ) { s += (a[i] - '0') * t; j ++, t *= 10; if (j == 9 || i == 0) { A.push_back(s); s = j = 0; t = 1; } } for (int i = b.size() - 1, s = 0, j = 0, t = 1; i >= 0; i -- ) { s += (b[i] - '0') * t; j ++, t *= 10; if (j == 9 || i == 0) { B.push_back(s); s = j = 0; t = 1; } }
auto C = add(A, B);
cout << C.back(); for (int i = C.size() - 2; i >= 0; i -- ) printf("%09d", C[i]); cout << endl;
// 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表示借位 t = A[i] - t; // 先减去借位 if (i < B.size()) t -= B[i]; // 注意判断较小数的位数 C.push_back((t + 10) % 10); // (a + r) % r得到的余数一定是正余数 if (t < 0) t = 1; // t < 0说明借位了,借位一定是借了1 else t = 0; // 没借位则t为0 } while (C.size() > 1 && C.back() == 0) C.pop_back(); // 注意需要处理前导零的问题(这里高位在后,前导零也在最后),但当只有一位是0必须保留 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); if (t < 0) t = 1; else t = 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; }
// C = A * b, A >= 0, b >= 0 vector<int> mul(vector<int> &A, int b){ vector<int> C; int t = 0; // t 用来表示进位 for (int i = 0; i < A.size() || t; ++ i){ // 注意把最后处理进位的问题合并到了一起,只要t不为0最后就会执行下去 if (i < A.size()) t += A[i] * b; // 注意这里是高精度乘低精度,所以直接用低精度数乘以每一位即可 C.push_back(t % 10); t /= 10; // 求出进位 }
vector<int> mul(vector<int> &A, vector<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; }
// C = A * b, A >= 0, b >= 0 vector<int> mul(vector<int> &A, int b){ vector<int> C; int t = 0; // t 用来表示进位 for (int i = 0; i < A.size() || t; ++ i){ // 注意把最后处理进位的问题合并到了一起,只要t不为0最后就会执行下去 if (i < A.size()) t += A[i] * b; // 注意这里是高精度乘低精度,所以直接用低精度数乘以每一位即可 C.push_back(t % 10); t /= 10; // 求出进位 }
intmain(){ scanf("%d%d", &n, &k); for (int i = 1; i <= n; i ++ ){ scanf("%lld", &s[i]); s[i] += s[i - 1]; }
LL res = 0; cnt[0] = 1; for (int i = 1; i <= n; i ++ ){ int t = s[i] % k res += cnt[t] ++; // 利用同余性质:(a - b) % p = (a % p - b % p ) % p可以发现 // (S[r] - S[l]) % k == (S[r] % k - S[l] % k) % k // 又S[r] % k < k, S[l] % k < k必得出结论: // S[r]与S[l]要同余 // 结果中不用求解排列组合,而是利用每次循环,每多一个元素就增加原先元素数个K倍区间
// 返回x最右端一位的1所代表的大小 intlowbit(x){ return x & -x; }
intmain(){ int n; scanf("%d", &n) while (n --){ int x; scanf("%d", &x); int res = 0; while (x != 0){ x -= lowbit(x); // 每次减去x的最后一个1所代表的数值 ++ res; } printf("%d\n", res); } return0; }
// 二分求出x对应的离散化的值 intfind(int x){ // 找到第一个大于等于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, 2, ...n }
Python
不去重,重复数字离散化后不一样
1 2 3 4 5
b = sorted(a) for i range(len(b)): a[a.index(b[i])] = i + 1# 由于从1开始映射,需加1 # a.index(x)查找x在a中位置,从列表中获取指定索引元素的第一个匹配位置 # 由于a会被更新,前面重复的都被变成了相对位置,就避免了查找位置不变的问题
去重写法,重复数字离散化后一样
1 2 3 4
b = list(set(a)) # 利用集合去重 b.sort() for i inrange(len(a)): a[i] = b.index(a[i]) + 1# 为保持一致,就要查找不被改变的b,由于b的index就对应于相对大小顺序,所以直接加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);