STL 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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 vector, 变长数组,倍增的思想 size () 返回元素个数 empty () 返回是否为空 clear () 清空 front ()/back () push_back ()/pop_back () begin ()/end () [] 支持比较运算,按字典序 pair<int , int > first, 第一个元素 second, 第二个元素 支持比较运算,以first为第一关键字,以second为第二关键字(字典序) string,字符串 size ()/length () 返回字符串长度 empty () clear () substr (起始下标,(子串长度)) 返回子串 c_str () 返回字符串所在字符数组的起始地址 queue, 队列 size () empty () push () 向队尾插入一个元素 front () 返回队头元素 back () 返回队尾元素 pop () 弹出队头元素 priority_queue, 优先队列,默认是大根堆 size () empty () push () 插入一个元素 top () 返回堆顶元素 pop () 弹出堆顶元素 定义成小根堆的方式:priority_queue<int , vector<int >, greater<int >> q; stack, 栈 size () empty () push () 向栈顶插入一个元素 top () 返回栈顶元素 pop () 弹出栈顶元素 deque, 双端队列 size () empty () clear () front ()/back () push_back ()/pop_back () push_front ()/pop_front () begin ()/end () [] set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列 size () empty () clear () begin ()/end () ++, -- 返回前驱和后继,时间复杂度 O (logn) set/multiset insert () 插入一个数 find () 查找一个数 count () 返回某一个数的个数 erase () (1 ) 输入是一个数x,删除所有x O (k + logn) (2 ) 输入一个迭代器,删除这个迭代器 lower_bound () /upper_bound () lower_bound (x) 返回大于等于x的最小的数的迭代器 upper_bound (x) 返回大于x的最小的数的迭代器 map/multimap insert () 插入的数是一个pair erase () 输入的参数是pair或者迭代器 find () [] 注意multimap不支持此操作。 时间复杂度是 O (logn) lower_bound () /upper_bound () unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表 和上面类似,增删改查的时间复杂度是 O (1 ) 不支持 lower_bound () /upper_bound () , 迭代器的++,-- bitset, 圧位 bitset<10000> s ; ~, &, |, ^ >>, << ==, != [] count () 返回有多少个1 any () 判断是否至少有一个1 none () 判断是否全为0 set () 把所有位置成1 set (k, v) 将第k位变成v reset () 把所有位变成0 flip () 等价于~ flip (k) 把第k位取反
快排 模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void quick_sort (int q[], int l, int r) { if (l >= r) return ; int i = l - 1 , j = r + 1 , x = q[l + r >> 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); }
例题 给定你一个长度为 nn 的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式 输入共两行,第一行包含整数 nn。
第二行包含 nn 个整数(所有整数均在 1∼1091∼109 范围内),表示整个数列。
输出格式 输出共一行,包含 nn 个整数,表示排好序的数列。
数据范围 1≤n≤1000001≤n≤100000
输入样例:
输出样例:
解题: 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 #include <iostream> using namespace std;const int N=1000010 ;int q[N];void quick_sort (int q[],int l,int r) { if (l>=r)return ; int x=q[l+r>>1 ],i=l-1 ,j=r+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); } int main () { int n; scanf ("%d" ,&n); for (int i=0 ;i<n;i++) scanf ("%d" ,&q[i]); quick_sort (q,0 ,n-1 ); for (int i=0 ;i<n;i++) printf ("%d " ,q[i]); return 0 ; }
归并排序 模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void merge_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 , tmp[r - l + 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 (k = 0 , i = l; i <= r; k++, i++) q[i] = tmp[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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> using namespace std;const int N=1000010 ;int q[N],tmp[N];void merge_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]; } int main () { int n;C++ 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]); return 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 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; }
例题 给定一个按照升序排列的长度为 nn 的整数数组,以及 qq 个查询。
对于每个查询,返回一个元素 kk 的起始位置和终止位置(位置从 00 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
输入格式 第一行包含整数 nn 和 qq,表示数组长度和询问个数。
第二行包含 nn 个整数(均在 1∼100001∼10000 范围内),表示完整数组。
接下来 qq 行,每行包含一个整数 kk,表示一个询问元素。
输出格式 共 qq 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围 1≤n≤1000001≤n≤100000 1≤q≤100001≤q≤10000 1≤k≤100001≤k≤10000
输入样例:
输出样例:
exp
在数组中查找某元素,找不到就输出-1,找到了就输出不小于该元素的最小位置和不大于该元素的最大位置。所以,需要写两个二分,一个需要找到>=x的第一个数,另一个需要找到<=x的最后一个数。
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 #include <iostream> using namespace std;const int N=1000010 ;int q[N];int n,m;int main () { scanf ("%d%d" ,&n,&m); for (int i=0 ;i<n;i++) scanf ("%d" ,&q[i]); while (m--) { int x; scanf ("%d" ,&x); int l=0 ,r=n-1 ; while (l<r) { int mid =l+r >>1 ; if (q[mid]>=x) r=mid; else l=mid+1 ; } if (q[l]!=x)cout<<"-1 -1" <<endl; else { cout<<l<<' ' ; int l=0 ,r=n-1 ; while (l<r) { int mid=l+r+1 >>1 ; if (q[mid]<=x) l=mid; else r=mid-1 ; } cout<<l<<endl; } } return 0 ; }
度减法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 ; } 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 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 ; } while (C.size () > 1 && C.back () == 0 ) C.pop_back (); }
高精度除以低精度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 (); r }
前缀和 一维
1 2 S[i] = a[1 ] + a[2 ] + ... a[i] a[l] + ... + a[r] = S[r] - S[l - 1 ]
二维
1 2 3 S[i, j] = 第i行j列格子左上部分所有元素的和 以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为: S[x2, y2] - S[x1 - 1 , y2] - S[x2, y1 - 1 ] + S[x1 - 1 , y1 - 1 ]
差分 一维
1 给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1 ] -= c
二维
1 2 给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c: S[x1, y1] += c, S[x2 + 1 , y1] -= c, S[x1, y2 + 1 ] -= c, S[x2 + 1 , y2 + 1 ] += c

b[x1][ y1 ] +=c ; 对应图1 ,让整个a数组中蓝色矩形面积的元素都加上了c。 b[x1,][y2+1]-=c ; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c,使其内元素不发生改变。 b[x2+1][y1]- =c ; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c,使其内元素不发生改变。 b[x2+1][y2+1]+=c; 对应图4,,让整个a数组中红色矩形面积的元素再加上c,红色内的相当于被减了两次,再加上一次c,才能使其恢复。
双指针算法 1 2 3 4 5 6 7 8 9 for (int i = 0 , j = 0 ; i < n; i ++ ){ while (j < i && check (i, j)) j ++ ; } 常见问题分类: (1 ) 对于一个序列,用两个指针维护一段区间 (2 ) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
离散化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vector<int > alls; sort (alls.begin (), alls.end ()); alls.erase (unique (alls.begin (), alls.end ()), alls.end ()); int find (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 ; }
例题: 假定有一个无限长的数轴,数轴上每个坐标上的数都是 00。
现在,我们首先进行 nn 次操作,每次操作将某一位置 xx 上的数加 cc。
接下来,进行 mm 次询问,每个询问包含两个整数 ll 和 rr,你需要求出在区间 [l,r][l,r] 之间的所有数的和。
输入格式 第一行包含两个整数 nn 和 mm。
接下来 nn 行,每行包含两个整数 xx 和 cc。
再接下来 mm 行,每行包含两个整数 ll 和 rr。
输出格式 共 mm 行,每行输出一个询问中所求的区间内数字和。
数据范围 −109≤x≤109−109≤x≤109, 1≤n,m≤1051≤n,m≤105, −109≤l≤r≤109−109≤l≤r≤109, −10000≤c≤10000−10000≤c≤10000
输入样例: 1 2 3 4 5 6 7 3 3 1 2 3 6 7 5 1 3 4 6 7 8
输出样例:
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 #include <iostream> #include <vector> #include <algorithm> using namespace std;typedef pair<int , int > PII;const int N = 300010 ;int a[N], s[N];int n, m;vector<int > alls; vector<PII> add, query; int find (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 ; } vector<int >:: iterator unique (vector<int > &a) { int j = 0 ; for (int i = 0 ; i < a.size (); i ++) if (!i || a[i] != a[i - 1 ]) a[j ++ ] = a[i]; return a.begin () + j; } int main () { cin >> n >> m; for (int i = 0 ; i < n; i ++ ) { int x, c; cin >> x >> c; add.push_back ({x, c}); alls.push_back (x); } for (int i = 0 ; i < m; i ++ ) { int l, r; cin >> l >> r; query.push_back ({l, r}); alls.push_back (l); alls.push_back (r); } sort (alls.begin (), alls.end ()); alls.erase (unique (alls), 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 : query) { int l = find (item.first), r = find (item.second); cout << s[r] - s[l - 1 ] << endl; } return 0 ; }
区间合并 1)把要合并的区间按区间左端点从小到大排序 2)用st和ed指针从前往后维护区间 3)比较ed 和后一个区间的左端点,分情况更新ed和first
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void merge (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; }