diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d47e129..b3db7e9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,7 +25,7 @@ jobs: SSH_PRIVATE_KEY: ${{ secrets.SSH_KEY }} ARGS: '-rltgoDzvO --delete' SOURCE: '.output/' - REMOTE_HOST: '81.68.108.130' + REMOTE_HOST: '182.92.222.231' REMOTE_USER: root TARGET: '/home/blog' # 打包后的 dist 文件夹将放在 # EXCLUDE: "node_modules,.git,.github" diff --git "a/public/_blogs/AcWing/C++ STL\347\256\200\344\273\213.md" "b/public/_blogs/AcWing/C++ STL\347\256\200\344\273\213.md" new file mode 100644 index 0000000..4e6e860 --- /dev/null +++ "b/public/_blogs/AcWing/C++ STL\347\256\200\344\273\213.md" @@ -0,0 +1,128 @@ +# C++ STL简介 + +```c++ +vector, 变长数组,倍增的思想 + size() 返回元素个数 + empty() 返回是否为空 + clear() 清空 + front()/back() + push_back()/pop_back() + begin()/end() + [] + 支持比较运算,按字典序 + +pair + 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, greater> 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位取反 +``` + +C++的STL(Standard Template Library,标准模板库)是C++标准库的一个重要组成部分。它提供了一组通用的、可重用的数据结构和算法,这些数据结构和算法以模板的形式实现,因此可以用于多种数据类型。 + +STL主要由以下几个部分组成: + +1. **容器**(Containers): + - 容器是用来存储对象的类模板,如`vector`、`list`、`deque`、`set`、`map`等。它们提供了不同的性能特征和访问模式。 + - 容器分为序列容器(如`vector`和`deque`)、关联容器(如`set`和`map`)、不规则容器(如`list`)以及辅助容器(如`array`)。 +2. **迭代器**(Iterators): + - 迭代器类似于指针,用于遍历容器中的元素。 + - STL定义了五种不同类型的迭代器:输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。 +3. **算法**(Algorithms): + - 算法是一系列作用于容器上的函数模板,例如`sort`、`find`、`copy`等。 + - 这些算法通过接受迭代器作为参数来操作容器中的元素,使得可以在任何容器上使用相同的算法而无需修改代码。 +4. **函数对象**(Function Objects): + - 也称为仿函数,是一种特殊的类实例,可以像函数一样被调用。 + - 函数对象通常用于定制算法的行为,比如排序时指定自定义比较逻辑。 +5. **分配器**(Allocators): + - 分配器负责管理内存的分配和释放,为容器提供高效地创建和销毁对象的能力。 + - 默认情况下,STL容器使用`std::allocator`,但用户也可以提供自己的分配器以适应特定需求。 + +STL的设计原则之一是分离容器与算法,这使得开发者可以灵活选择最适合他们需求的组件。例如,你可以在`vector`或`list`上使用相同的排序算法,而不需要关心底层容器的具体实现细节。 + +由于STL的广泛适用性和效率,它是C++编程中不可或缺的一部分,极大地提高了开发者的生产力。 \ No newline at end of file diff --git a/public/_blogs/AcWing/KMP.md b/public/_blogs/AcWing/KMP.md new file mode 100644 index 0000000..4f60f3e --- /dev/null +++ b/public/_blogs/AcWing/KMP.md @@ -0,0 +1,64 @@ +# KMP + +```c++ +// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度 +求模式串的Next数组: +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; +} + +// 匹配 +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) + { + j = ne[j]; + // 匹配成功后的逻辑 + } +} +``` + +* KMP字符串 + +```c++ +#include + +using namespace std; + +const int N = 100010, M = 1000010; + +int n, m; +int ne[N]; +char s[M], p[N]; + +int main() +{ + cin >> n >> p + 1 >> m >> s + 1; + + for (int i = 2, j = 0; i <= n; i ++ ) + { + while (j && p[i] != p[j + 1]) j = ne[j]; + if (p[i] == p[j + 1]) j ++ ; + ne[i] = j; + } + + for (int i = 1, j = 0; i <= m; i ++ ) + { + while (j && s[i] != p[j + 1]) j = ne[j]; + if (s[i] == p[j + 1]) j ++ ; + if (j == n) + { + printf("%d ", i - n); + j = ne[j]; + } + } + + return 0; +} +``` + diff --git a/public/_blogs/AcWing/TIRE.md b/public/_blogs/AcWing/TIRE.md new file mode 100644 index 0000000..435b294 --- /dev/null +++ b/public/_blogs/AcWing/TIRE.md @@ -0,0 +1,139 @@ +# TIRE + +```c++ +int son[N][26], cnt[N], idx; +// 0号点既是根节点,又是空节点 +// son[][]存储树中每个节点的子节点 +// cnt[]存储以每个节点结尾的单词数量 + +// 插入一个字符串 +void insert(char *str) +{ + int p = 0; + for (int i = 0; str[i]; i ++ ) + { + int u = str[i] - 'a'; + if (!son[p][u]) son[p][u] = ++ idx; + p = son[p][u]; + } + cnt[p] ++ ; +} + +// 查询字符串出现的次数 +int query(char *str) +{ + int p = 0; + for (int i = 0; str[i]; i ++ ) + { + int u = str[i] - 'a'; + if (!son[p][u]) return 0; + p = son[p][u]; + } + return cnt[p]; +} +``` + +* Tire字符串统计 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int son[N][26], cnt[N], idx; +char str[N]; + +void insert(char *str) +{ + int p = 0; + for (int i = 0; str[i]; i ++ ) + { + int u = str[i] - 'a'; + if (!son[p][u]) son[p][u] = ++ idx; + p = son[p][u]; + } + cnt[p] ++ ; +} + +int query(char *str) +{ + int p = 0; + for (int i = 0; str[i]; i ++ ) + { + int u = str[i] - 'a'; + if (!son[p][u]) return 0; + p = son[p][u]; + } + return cnt[p]; +} + +int main() +{ + int n; + scanf("%d", &n); + while (n -- ) + { + char op[2]; + scanf("%s%s", op, str); + if (*op == 'I') insert(str); + else printf("%d\n", query(str)); + } + + return 0; +} +``` + +* 最大异或对 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int son[N][26], cnt[N], idx; +char str[N]; + +void insert(char *str) +{ + int p = 0; + for (int i = 0; str[i]; i ++ ) + { + int u = str[i] - 'a'; + if (!son[p][u]) son[p][u] = ++ idx; + p = son[p][u]; + } + cnt[p] ++ ; +} + +int query(char *str) +{ + int p = 0; + for (int i = 0; str[i]; i ++ ) + { + int u = str[i] - 'a'; + if (!son[p][u]) return 0; + p = son[p][u]; + } + return cnt[p]; +} + +int main() +{ + int n; + scanf("%d", &n); + while (n -- ) + { + char op[2]; + scanf("%s%s", op, str); + if (*op == 'I') insert(str); + else printf("%d\n", query(str)); + } + + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\344\275\215\350\277\220\347\256\227.md" "b/public/_blogs/AcWing/\344\275\215\350\277\220\347\256\227.md" new file mode 100644 index 0000000..e13bd15 --- /dev/null +++ "b/public/_blogs/AcWing/\344\275\215\350\277\220\347\256\227.md" @@ -0,0 +1,33 @@ +# 位运算 + +``` +求n的第k位数字: n >> k & 1 +返回n的最后一位1:lowbit(n) = n & -n +``` + +* 二进制中1的个数 + + ```c++ + #include + + using namespace std; + + int main() + { + int n; + scanf("%d", &n); + while (n -- ) + { + int x, s = 0; + scanf("%d", &x); + + for (int i = x; i; i -= i & -i) s ++ ; + + printf("%d ", s); + } + + return 0; + } + ``` + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\345\211\215\347\274\200\345\222\214\344\270\216\345\267\256\345\210\206.md" "b/public/_blogs/AcWing/\345\211\215\347\274\200\345\222\214\344\270\216\345\267\256\345\210\206.md" new file mode 100644 index 0000000..e57def4 --- /dev/null +++ "b/public/_blogs/AcWing/\345\211\215\347\274\200\345\222\214\344\270\216\345\267\256\345\210\206.md" @@ -0,0 +1,182 @@ +# 前缀和与差分 + +1. 前缀和 + + 1. 一维前缀和 + + ``` + S[i] = a[1] + a[2] + ... a[i] + a[l] + ... + a[r] = S[r] - S[l - 1] + ``` + + ``` + #include + + using namespace std; + + const int N = 100010; + + int n, m; + int a[N], s[N]; + + int main() + { + scanf("%d%d", &n, &m); + for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]); + + for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; // 前缀和的初始化 + + while (m -- ) + { + int l, r; + scanf("%d%d", &l, &r); + printf("%d\n", s[r] - s[l - 1]); // 区间和的计算 + } + + return 0; + } + ``` + + 2. 二维前缀和 + + ``` + 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] + ``` + + ```c++ + #include + + using namespace std; + + const int N = 1010; + + int n, m, q; + int a[N][N], s[N][N]; + + int main() { + cin >> n >> m >> q; + + for (int i = 1; i <= n; i++) + for (int j = 1; j <= m; j++) { + scanf("%d", &a[i][j]); + s[i][j] = s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1] + a[i][j]; // 求前缀和 + } + + 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; + } + ``` + +2. 差分 + + 1. 一维差分 + + ``` + 给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c + ``` + + ```c++ + #include + + using namespace std; + + const int N = 100010; + + int n, m; + int a[N], b[N]; + + void insert(int l, int r, int c) + { + b[l] += c; + b[r + 1] -= c; + } + + int main() + { + scanf("%d%d", &n, &m); + for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]); + + for (int i = 1; i <= n; i ++ ) insert(i, i, a[i]); + + while (m -- ) + { + int l, r, c; + scanf("%d%d%d", &l, &r, &c); + insert(l, r, c); + } + + for (int i = 1; i <= n; i ++ ) b[i] += b[i - 1]; + + for (int i = 1; i <= n; i ++ ) printf("%d ", b[i]); + + return 0; + } + ``` + + 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 + ``` + + ```c++ + #include + + using namespace std; + + const int N = 1010; + + int n, m, q; + 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() + { + scanf("%d%d%d", &n, &m, &q); + + for (int i = 1; i <= n; i ++ ) + for (int j = 1; j <= m; j ++ ) + scanf("%d", &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]); + puts(""); + } + + return 0; + } + ``` + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\345\214\272\351\227\264\345\220\210\345\271\266.md" "b/public/_blogs/AcWing/\345\214\272\351\227\264\345\220\210\345\271\266.md" new file mode 100644 index 0000000..bd4750d --- /dev/null +++ "b/public/_blogs/AcWing/\345\214\272\351\227\264\345\220\210\345\271\266.md" @@ -0,0 +1,78 @@ +# 区间合并 + +```c++ +// 将所有存在交集的区间合并 +void merge(vector &segs) +{ + vector 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; +} +``` + +* 区间合并 + + ```C++ + #include + #include + #include + + using namespace std; + + typedef pair PII; + + void merge(vector &segs) + { + vector 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; + } + + int main() + { + int n; + scanf("%d", &n); + + vector segs; + for (int i = 0; i < n; i ++ ) + { + int l, r; + scanf("%d%d", &l, &r); + segs.push_back({l, r}); + } + + merge(segs); + + cout << segs.size() << endl; + + return 0; + } + ``` + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\345\215\225\350\260\203\346\240\210.md" "b/public/_blogs/AcWing/\345\215\225\350\260\203\346\240\210.md" new file mode 100644 index 0000000..f7c5f03 --- /dev/null +++ "b/public/_blogs/AcWing/\345\215\225\350\260\203\346\240\210.md" @@ -0,0 +1,41 @@ +# 单调栈 + +```c++ +常见模型:找出每个数左边离它最近的比它大/小的数 +int tt = 0; +for (int i = 1; i <= n; i ++ ) +{ + while (tt && check(stk[tt], i)) tt -- ; + stk[ ++ tt] = i; +} +``` + +* 单调栈 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int stk[N], tt; + +int main() +{ + int n; + cin >> n; + while (n -- ) + { + int x; + scanf("%d", &x); + while (tt && stk[tt] >= x) tt -- ; + if (!tt) printf("-1 "); + else printf("%d ", stk[tt]); + stk[ ++ tt] = x; + } + + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\345\215\225\350\260\203\351\230\237\345\210\227.md" "b/public/_blogs/AcWing/\345\215\225\350\260\203\351\230\237\345\210\227.md" new file mode 100644 index 0000000..07beb2f --- /dev/null +++ "b/public/_blogs/AcWing/\345\215\225\350\260\203\351\230\237\345\210\227.md" @@ -0,0 +1,60 @@ +# 单调队列 + +```c++ +常见模型:找出滑动窗口中的最大值/最小值 +int hh = 0, tt = -1; +for (int i = 0; i < n; i ++ ) +{ + while (hh <= tt && check_out(q[hh])) hh ++ ; // 判断队头是否滑出窗口 + while (hh <= tt && check(q[tt], i)) tt -- ; + q[ ++ tt] = i; +} +``` + +* 滑动窗口 + +```c++ +#include + +using namespace std; + +const int N = 1000010; + +int a[N], q[N]; + +int main() +{ + int n, k; + scanf("%d%d", &n, &k); + for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]); + + int hh = 0, tt = -1; + for (int i = 0; i < n; i ++ ) + { + if (hh <= tt && i - k + 1 > q[hh]) hh ++ ; + + while (hh <= tt && a[q[tt]] >= a[i]) tt -- ; + q[ ++ tt] = i; + + if (i >= k - 1) printf("%d ", a[q[hh]]); + } + + puts(""); + + hh = 0, tt = -1; + for (int i = 0; i < n; i ++ ) + { + if (hh <= tt && i - k + 1 > q[hh]) hh ++ ; + + while (hh <= tt && a[q[tt]] <= a[i]) tt -- ; + q[ ++ tt] = i; + + if (i >= k - 1) printf("%d ", a[q[hh]]); + } + + puts(""); + + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\345\215\225\351\223\276\350\241\250.md" "b/public/_blogs/AcWing/\345\215\225\351\223\276\350\241\250.md" new file mode 100644 index 0000000..b72723f --- /dev/null +++ "b/public/_blogs/AcWing/\345\215\225\351\223\276\350\241\250.md" @@ -0,0 +1,106 @@ +# 单链表 + +```C++ +// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点 +int head, e[N], ne[N], idx; + +// 初始化 +void init() +{ + head = -1; + idx = 0; +} + +// 在链表头插入一个数a +void insert(int a) +{ + e[idx] = a, ne[idx] = head, head = idx ++ ; +} + +// 将头结点删除,需要保证头结点存在 +void remove() +{ + head = ne[head]; +} +``` + +* 单链表 + + ```c++ + #include + + using namespace std; + + const int N = 100010; + + + // head 表示头结点的下标 + // e[i] 表示节点i的值 + // ne[i] 表示节点i的next指针是多少 + // idx 存储当前已经用到了哪个点 + int head, e[N], ne[N], idx; + + // 初始化 + void init() + { + head = -1; + idx = 0; + } + + // 将x插到头结点 + void add_to_head(int x) + { + e[idx] = x, ne[idx] = head, head = idx ++ ; + } + + // 将x插到下标是k的点后面 + void add(int k, int x) + { + e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ; + } + + // 将下标是k的点后面的点删掉 + void remove(int k) + { + ne[k] = ne[ne[k]]; + } + + int main() + { + int m; + cin >> m; + + init(); + + while (m -- ) + { + int k, x; + char op; + + cin >> op; + if (op == 'H') + { + cin >> x; + add_to_head(x); + } + else if (op == 'D') + { + cin >> k; + if (!k) head = ne[head]; + else remove(k - 1); + } + else + { + cin >> k >> x; + add(k - 1, x); + } + } + + for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' '; + cout << endl; + + return 0; + } + ``` + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\345\217\214\346\214\207\351\222\210\347\256\227\346\263\225.md" "b/public/_blogs/AcWing/\345\217\214\346\214\207\351\222\210\347\256\227\346\263\225.md" new file mode 100644 index 0000000..d454189 --- /dev/null +++ "b/public/_blogs/AcWing/\345\217\214\346\214\207\351\222\210\347\256\227\346\263\225.md" @@ -0,0 +1,110 @@ +# 双指针算法 + +降低时间复杂度:$O(n^2)——>O(n)$ + +```c++ +for (int i = 0, j = 0; i < n; i ++ ) +{ + while (j < i && check(i, j)) j ++ ; + + // 具体问题的逻辑 +} +常见问题分类: + (1) 对于一个序列,用两个指针维护一段区间 + (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作 +``` + +* **最长连续不重复子序列** + + ```c++ + #include + + using namespace std; + + const int N = 100010; + + int n; + int q[N], s[N]; + + int main() + { + scanf("%d", &n); + for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); + + int res = 0; + for (int i = 0, j = 0; i < n; i ++ ) + { + s[q[i]] ++ ; + while (j < i && s[q[i]] > 1) s[q[j ++ ]] -- ; + res = max(res, i - j + 1); + } + + cout << res << endl; + + return 0; + } + ``` + +* **数组元素的目标和** + + ```C++ + #include + + using namespace std; + + const int N = 1e5 + 10; + + int n, m, x; + int a[N], b[N]; + + int main() + { + scanf("%d%d%d", &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; + } + + return 0; + } + ``` + +* **判断子序列** + + ```C++ + #include + #include + + using namespace std; + + const int N = 100010; + + int n, m; + int a[N], b[N]; + + int main() + { + scanf("%d%d", &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"); + else puts("No"); + + return 0; + } + ``` + + + diff --git "a/public/_blogs/AcWing/\345\217\214\351\223\276\350\241\250.md" "b/public/_blogs/AcWing/\345\217\214\351\223\276\350\241\250.md" new file mode 100644 index 0000000..511c1fe --- /dev/null +++ "b/public/_blogs/AcWing/\345\217\214\351\223\276\350\241\250.md" @@ -0,0 +1,156 @@ +# 双链表 + +```c++ +#include + +using namespace std; + +const int N = 100010; + + +// head 表示头结点的下标 +// e[i] 表示节点i的值 +// ne[i] 表示节点i的next指针是多少 +// idx 存储当前已经用到了哪个点 +int head, e[N], ne[N], idx; + +// 初始化 +void init() +{ + head = -1; + idx = 0; +} + +// 将x插到头结点 +void add_to_head(int x) +{ + e[idx] = x, ne[idx] = head, head = idx ++ ; +} + +// 将x插到下标是k的点后面 +void add(int k, int x) +{ + e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ; +} + +// 将下标是k的点后面的点删掉 +void remove(int k) +{ + ne[k] = ne[ne[k]]; +} + +int main() +{ + int m; + cin >> m; + + init(); + + while (m -- ) + { + int k, x; + char op; + + cin >> op; + if (op == 'H') + { + cin >> x; + add_to_head(x); + } + else if (op == 'D') + { + cin >> k; + if (!k) head = ne[head]; + else remove(k - 1); + } + else + { + cin >> k >> x; + add(k - 1, x); + } + } + + for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' '; + cout << endl; + + return 0; +} +``` + +* 双链表 + + ```c++ + #include + + using namespace std; + + const int N = 100010; + + int m; + int e[N], l[N], r[N], idx; + + // 在节点a的右边插入一个数x + void insert(int a, int x) + { + e[idx] = x; + l[idx] = a, r[idx] = r[a]; + l[r[a]] = idx, r[a] = idx ++ ; + } + + // 删除节点a + void remove(int a) + { + l[r[a]] = l[a]; + r[l[a]] = r[a]; + } + + int main() + { + cin >> m; + + // 0是左端点,1是右端点 + r[0] = 1, l[1] = 0; + idx = 2; + + while (m -- ) + { + string op; + cin >> op; + int k, x; + if (op == "L") + { + cin >> x; + insert(0, x); + } + else if (op == "R") + { + cin >> x; + insert(l[1], x); + } + else if (op == "D") + { + cin >> k; + remove(k + 1); + } + else if (op == "IL") + { + cin >> k >> x; + insert(l[k + 1], x); + } + else + { + cin >> k >> x; + insert(k + 1, x); + } + } + + for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' '; + cout << endl; + + return 0; + } + ``` + + + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\345\223\210\345\270\214\350\241\250.md" "b/public/_blogs/AcWing/\345\223\210\345\270\214\350\241\250.md" new file mode 100644 index 0000000..220b10f --- /dev/null +++ "b/public/_blogs/AcWing/\345\223\210\345\270\214\350\241\250.md" @@ -0,0 +1,218 @@ +# 哈希表 + +1. 一般哈希 + +```c++ +(1) 拉链法 + int h[N], e[N], ne[N], idx; + + // 向哈希表中插入一个数 + void insert(int x) + { + int k = (x % N + N) % N; + e[idx] = x; + ne[idx] = h[k]; + h[k] = idx ++ ; + } + + // 在哈希表中查询某个数是否存在 + bool find(int x) + { + int k = (x % N + N) % N; + for (int i = h[k]; i != -1; i = ne[i]) + if (e[i] == x) + return true; + + return false; + } + +(2) 开放寻址法 + int h[N]; + + // 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置 + int find(int x) + { + int t = (x % N + N) % N; + while (h[t] != null && h[t] != x) + { + t ++ ; + if (t == N) t = 0; + } + return t; + } +``` + +* 模拟散列表 + +##### 开放寻址法 + +```c++ +#include +#include + +using namespace std; + +const int N = 200003, null = 0x3f3f3f3f; + +int h[N]; + +int find(int x) +{ + int t = (x % N + N) % N; + while (h[t] != null && h[t] != x) + { + t ++ ; + if (t == N) t = 0; + } + return t; +} + +int main() +{ + memset(h, 0x3f, sizeof h); + + int n; + scanf("%d", &n); + + while (n -- ) + { + char op[2]; + int x; + scanf("%s%d", op, &x); + if (*op == 'I') h[find(x)] = x; + else + { + if (h[find(x)] == null) puts("No"); + else puts("Yes"); + } + } + + return 0; +} +``` + +##### 拉链法 + +```c++ +#include +#include + +using namespace std; + +const int N = 100003; + +int h[N], e[N], ne[N], idx; + +void insert(int x) +{ + int k = (x % N + N) % N; + e[idx] = x; + ne[idx] = h[k]; + h[k] = idx ++ ; +} + +bool find(int x) +{ + int k = (x % N + N) % N; + for (int i = h[k]; i != -1; i = ne[i]) + if (e[i] == x) + return true; + + return false; +} + +int main() +{ + int n; + scanf("%d", &n); + + memset(h, -1, sizeof h); + + while (n -- ) + { + char op[2]; + int x; + scanf("%s%d", op, &x); + + if (*op == 'I') insert(x); + else + { + if (find(x)) puts("Yes"); + else puts("No"); + } + } + + return 0; +} +``` + +2. 字符串哈希 + +```c++ +核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低 +小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果 + +typedef unsigned long long ULL; +ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64 + +// 初始化 +p[0] = 1; +for (int i = 1; i <= n; i ++ ) +{ + h[i] = h[i - 1] * P + str[i]; + p[i] = p[i - 1] * P; +} + +// 计算子串 str[l ~ r] 的哈希值 +ULL get(int l, int r) +{ + return h[r] - h[l - 1] * p[r - l + 1]; +} +``` + +* 字符串哈希 + +```c++ +#include +#include + +using namespace std; + +typedef unsigned long long ULL; + +const int N = 100010, P = 131; + +int n, m; +char str[N]; +ULL h[N], p[N]; + +ULL get(int l, int r) +{ + return h[r] - h[l - 1] * p[r - l + 1]; +} + +int main() +{ + scanf("%d%d", &n, &m); + scanf("%s", str + 1); + + p[0] = 1; + for (int i = 1; i <= n; i ++ ) + { + h[i] = h[i - 1] * P + str[i]; + p[i] = p[i - 1] * P; + } + + while (m -- ) + { + int l1, r1, l2, r2; + scanf("%d%d%d%d", &l1, &r1, &l2, &r2); + + if (get(l1, r1) == get(l2, r2)) puts("Yes"); + else puts("No"); + } + + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\345\240\206.md" "b/public/_blogs/AcWing/\345\240\206.md" new file mode 100644 index 0000000..7226629 --- /dev/null +++ "b/public/_blogs/AcWing/\345\240\206.md" @@ -0,0 +1,176 @@ +# 堆 + +```c++ +// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1 +// ph[k]存储第k个插入的点在堆中的位置 +// hp[k]存储堆中下标是k的点是第几个插入的 +int h[N], ph[N], hp[N], size; + +// 交换两个点,及其映射关系 +void heap_swap(int a, int b) +{ + swap(ph[hp[a]],ph[hp[b]]); + swap(hp[a], hp[b]); + swap(h[a], h[b]); +} + +void down(int u) +{ + int t = u; + if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2; + if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1; + if (u != t) + { + heap_swap(u, t); + down(t); + } +} + +void up(int u) +{ + while (u / 2 && h[u] < h[u / 2]) + { + heap_swap(u, u / 2); + u >>= 1; + } +} + +// O(n)建堆 +for (int i = n / 2; i; i -- ) down(i); +``` + +* 堆排序 + +```c++ +#include +#include + +using namespace std; + +const int N = 100010; + +int n, m; +int h[N], cnt; + +void down(int u) +{ + int t = u; + if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2; + if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1; + if (u != t) + { + swap(h[u], h[t]); + down(t); + } +} + +int main() +{ + scanf("%d%d", &n, &m); + for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]); + cnt = n; + + for (int i = n / 2; i; i -- ) down(i); + + while (m -- ) + { + printf("%d ", h[1]); + h[1] = h[cnt -- ]; + down(1); + } + + puts(""); + + return 0; +} +``` + +* 模拟堆 + +```c++ +#include +#include +#include + +using namespace std; + +const int N = 100010; + +int h[N], ph[N], hp[N], cnt; + +void heap_swap(int a, int b) +{ + swap(ph[hp[a]],ph[hp[b]]); + swap(hp[a], hp[b]); + swap(h[a], h[b]); +} + +void down(int u) +{ + int t = u; + if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2; + if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1; + if (u != t) + { + heap_swap(u, t); + down(t); + } +} + +void up(int u) +{ + while (u / 2 && h[u] < h[u / 2]) + { + heap_swap(u, u / 2); + u >>= 1; + } +} + +int main() +{ + int n, m = 0; + scanf("%d", &n); + while (n -- ) + { + char op[5]; + int k, x; + scanf("%s", op); + if (!strcmp(op, "I")) + { + scanf("%d", &x); + cnt ++ ; + m ++ ; + ph[m] = cnt, hp[cnt] = m; + h[cnt] = x; + up(cnt); + } + else if (!strcmp(op, "PM")) printf("%d\n", h[1]); + else if (!strcmp(op, "DM")) + { + heap_swap(1, cnt); + cnt -- ; + down(1); + } + else if (!strcmp(op, "D")) + { + scanf("%d", &k); + k = ph[k]; + heap_swap(k, cnt); + cnt -- ; + up(k); + down(k); + } + else + { + scanf("%d%d", &k, &x); + k = ph[k]; + h[k] = x; + up(k); + down(k); + } + } + + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\345\271\266\346\237\245\351\233\206.md" "b/public/_blogs/AcWing/\345\271\266\346\237\245\351\233\206.md" new file mode 100644 index 0000000..0a8ed60 --- /dev/null +++ "b/public/_blogs/AcWing/\345\271\266\346\237\245\351\233\206.md" @@ -0,0 +1,241 @@ +# 并查集 + +```c++ +(1)朴素并查集: + + int p[N]; //存储每个点的祖宗节点 + + // 返回x的祖宗节点 + int find(int x) + { + if (p[x] != x) p[x] = find(p[x]); + return p[x]; + } + + // 初始化,假定节点编号是1~n + for (int i = 1; i <= n; i ++ ) p[i] = i; + + // 合并a和b所在的两个集合: + p[find(a)] = find(b); + + +(2)维护size的并查集: + + int p[N], size[N]; + //p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量 + + // 返回x的祖宗节点 + int find(int x) + { + if (p[x] != x) p[x] = find(p[x]); + return p[x]; + } + + // 初始化,假定节点编号是1~n + for (int i = 1; i <= n; i ++ ) + { + p[i] = i; + size[i] = 1; + } + + // 合并a和b所在的两个集合: + size[find(b)] += size[find(a)]; + p[find(a)] = find(b); + + +(3)维护到祖宗节点距离的并查集: + + int p[N], d[N]; + //p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离 + + // 返回x的祖宗节点 + int find(int x) + { + if (p[x] != x) + { + int u = find(p[x]); + d[x] += d[p[x]]; + p[x] = u; + } + return p[x]; + } + + // 初始化,假定节点编号是1~n + for (int i = 1; i <= n; i ++ ) + { + p[i] = i; + d[i] = 0; + } + + // 合并a和b所在的两个集合: + p[find(a)] = find(b); + d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量 +``` + +* 合并集合 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int p[N]; + +int find(int x) +{ + if (p[x] != x) p[x] = find(p[x]); + return p[x]; +} + +int main() +{ + int n, m; + scanf("%d%d", &n, &m); + for (int i = 1; i <= n; i ++ ) p[i] = i; + + while (m -- ) + { + char op[2]; + int a, b; + scanf("%s%d%d", op, &a, &b); + if (*op == 'M') p[find(a)] = find(b); + else + { + if (find(a) == find(b)) puts("Yes"); + else puts("No"); + } + } + + return 0; +} +``` + +* 连通块中点的数量 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int n, m; +int p[N], cnt[N]; + +int find(int x) +{ + if (p[x] != x) p[x] = find(p[x]); + return p[x]; +} + +int main() +{ + cin >> n >> m; + + for (int i = 1; i <= n; i ++ ) + { + p[i] = i; + cnt[i] = 1; + } + + while (m -- ) + { + string op; + int a, b; + cin >> op; + + if (op == "C") + { + cin >> a >> b; + a = find(a), b = find(b); + if (a != b) + { + p[a] = b; + cnt[b] += cnt[a]; + } + } + else if (op == "Q1") + { + cin >> a >> b; + if (find(a) == find(b)) puts("Yes"); + else puts("No"); + } + else + { + cin >> a; + cout << cnt[find(a)] << endl; + } + } + + return 0; +} +``` + +* 食物链 + +```c++ +#include + +using namespace std; + +const int N = 50010; + +int n, m; +int p[N], d[N]; + +int find(int x) +{ + if (p[x] != x) + { + int t = find(p[x]); + d[x] += d[p[x]]; + p[x] = t; + } + return p[x]; +} + +int main() +{ + scanf("%d%d", &n, &m); + + for (int i = 1; i <= n; i ++ ) p[i] = i; + + int res = 0; + while (m -- ) + { + int t, x, y; + scanf("%d%d%d", &t, &x, &y); + + if (x > n || y > n) res ++ ; + else + { + int px = find(x), py = find(y); + if (t == 1) + { + if (px == py && (d[x] - d[y]) % 3) res ++ ; + else if (px != py) + { + p[px] = py; + d[px] = d[y] - d[x]; + } + } + else + { + if (px == py && (d[x] - d[y] - 1) % 3) res ++ ; + else if (px != py) + { + p[px] = py; + d[px] = d[y] + 1 - d[x]; + } + } + } + } + + printf("%d\n", res); + + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\345\277\253\351\200\237\346\216\222\345\272\217\344\270\216\345\275\222\345\271\266\346\216\222\345\272\217\344\270\216\344\272\214\345\210\206\346\237\245\346\211\276.md" "b/public/_blogs/AcWing/\345\277\253\351\200\237\346\216\222\345\272\217\344\270\216\345\275\222\345\271\266\346\216\222\345\272\217\344\270\216\344\272\214\345\210\206\346\237\245\346\211\276.md" index 7468666..086367e 100644 --- "a/public/_blogs/AcWing/\345\277\253\351\200\237\346\216\222\345\272\217\344\270\216\345\275\222\345\271\266\346\216\222\345\272\217\344\270\216\344\272\214\345\210\206\346\237\245\346\211\276.md" +++ "b/public/_blogs/AcWing/\345\277\253\351\200\237\346\216\222\345\272\217\344\270\216\345\275\222\345\271\266\346\216\222\345\272\217\344\270\216\344\272\214\345\210\206\346\237\245\346\211\276.md" @@ -94,11 +94,11 @@ void merge_sort(int q[], int l, int r) 如何分到单调性与非单调性的分界点 1. $mid=\frac{l+r+1}{2}$ - + $if(check(mid))\begin{cases}true&[mid,r]:l=mid\\false&[l,mid-1]:r=mid-1\end{cases}$ 2. $mid=\frac{l+r}{2}$ - + $if(check(mid))\begin{cases}true&[l,mid]:r=mid\\false&[mid+1,r]:l=mid+1\end{cases}$ --- @@ -157,7 +157,3 @@ double bsearch_3(double l, double r) return l; } ``` - - - - diff --git "a/public/_blogs/AcWing/\346\240\210.md" "b/public/_blogs/AcWing/\346\240\210.md" new file mode 100644 index 0000000..a694707 --- /dev/null +++ "b/public/_blogs/AcWing/\346\240\210.md" @@ -0,0 +1,118 @@ +# 栈 + +```c++ +// tt表示栈顶 +int stk[N], tt = 0; + +// 向栈顶插入一个数 +stk[ ++ tt] = x; + +// 从栈顶弹出一个数 +tt -- ; + +// 栈顶的值 +stk[tt]; + +// 判断栈是否为空,如果 tt > 0,则表示不为空 +if (tt > 0) +{ + +} +``` + +模拟栈 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int m; +int stk[N], tt; + +int main() +{ + cin >> m; + while (m -- ) + { + string op; + int x; + + cin >> op; + if (op == "push") + { + cin >> x; + stk[ ++ tt] = x; + } + else if (op == "pop") tt -- ; + else if (op == "empty") cout << (tt ? "NO" : "YES") << endl; + else cout << stk[tt] << endl; + } + + return 0; +} +``` + +* 表达式求值 + +```C++ +#include +#include +#include +#include +#include + +using namespace std; + +stack num; +stack op; + +void eval() +{ + auto b = num.top(); num.pop(); + auto a = num.top(); num.pop(); + auto c = op.top(); op.pop(); + int x; + if (c == '+') x = a + b; + else if (c == '-') x = a - b; + else if (c == '*') x = a * b; + else x = a / b; + num.push(x); +} + +int main() +{ + unordered_map pr{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; + string str; + cin >> str; + for (int i = 0; i < str.size(); i ++ ) + { + auto c = str[i]; + if (isdigit(c)) + { + int x = 0, j = i; + while (j < str.size() && isdigit(str[j])) + x = x * 10 + str[j ++ ] - '0'; + i = j - 1; + num.push(x); + } + else if (c == '(') op.push(c); + else if (c == ')') + { + while (op.top() != '(') eval(); + op.pop(); + } + else + { + while (op.size() && op.top() != '(' && pr[op.top()] >= pr[c]) eval(); + op.push(c); + } + } + while (op.size()) eval(); + cout << num.top() << endl; + return 0; +} +``` + diff --git "a/public/_blogs/AcWing/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\357\274\210DFS\357\274\211.md" "b/public/_blogs/AcWing/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\357\274\210DFS\357\274\211.md" index e1fab45..63165dd 100644 --- "a/public/_blogs/AcWing/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\357\274\210DFS\357\274\211.md" +++ "b/public/_blogs/AcWing/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\357\274\210DFS\357\274\211.md" @@ -2,10 +2,10 @@ ### BFS与DFS比较 -| | 数据结构 | 空间 | | -| ---- | --------- | ------ | ------ | -| DFS | stack(栈) | O(n) | 不具有最短性 | -| BFS | queue(队列) | O(2^n) | 最短路 | +| | 数据结构 | 空间 | | +| --- | --------- | ------ | ------ | +| DFS | stack(栈) | O(n) | 不具有最短性 | +| BFS | queue(队列) | O(2^n) | 最短路 | ### DFS—深度优先搜索 @@ -115,4 +115,3 @@ int main() ``` ### BFS—广度优先搜索 - diff --git "a/public/_blogs/AcWing/\347\246\273\346\225\243\345\214\226.md" "b/public/_blogs/AcWing/\347\246\273\346\225\243\345\214\226.md" new file mode 100644 index 0000000..4634675 --- /dev/null +++ "b/public/_blogs/AcWing/\347\246\273\346\225\243\345\214\226.md" @@ -0,0 +1,126 @@ +# 离散化 + +离散化的本质是建立了一段数列到自然数之间的映射关系(value -> index),通过建立新索引,来缩小目标区间,使得可以进行一系列连续数组可以进行的操作比如二分,前缀和等… + +离散化首先需要排序去重: + +```c++ +1. 排序:sort(alls.begin(),alls.end()) +2. 去重:alls.earse(unique(alls.begin(),alls.end()),alls.end()); +``` + +unique()函数的底层原理 + +```c++ +vector::iterator unique(vector &a) { + int j = 0; + for (int i = 0; i < a.size(); ++i) { + if (!i || a[i] != a[i - 1])//如果是第一个元素或者该元素不等于前一个元素,即不重复元素,我们就把它存到数组前j个元素中 + a[j++] = a[i];//每存在一个不同元素,j++ + } + return a.begin() + j;//返回的是前j个不重复元素的下标 +} +``` + +```c++ +vector alls; // 存储所有待离散化的值 +sort(alls.begin(), alls.end()); // 将所有值排序 +alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素 + +// 二分求出x对应的离散化的值 +int find(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 +} +``` + +* 区间和 + + ``` + 由于本题可能有多组数据是针对同一个数组下标操作的,因此我们可以将所有用到的数组下标装在一个下标容器alls内去重,然后再逐一为相同的数组下标增加数值c,再通过对应前缀和相减求得区间 l~r 之间的数的值 + ``` + + ```c++ + #include + #include + #include + + using namespace std; + + typedef pair PII; + + const int N = 300010; + + int n, m; + int a[N], s[N]; + + vector alls; + vector 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; + } + + 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.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 : query) + { + int l = find(item.first), r = find(item.second); + cout << s[r] - s[l - 1] << endl; + } + + return 0; + } + ``` + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\351\230\237\345\210\227.md" "b/public/_blogs/AcWing/\351\230\237\345\210\227.md" new file mode 100644 index 0000000..789a38c --- /dev/null +++ "b/public/_blogs/AcWing/\351\230\237\345\210\227.md" @@ -0,0 +1,87 @@ +# 队列 + +1. 普通队列 + +```C++ +// hh 表示队头,tt表示队尾 +int q[N], hh = 0, tt = -1; + +// 向队尾插入一个数 +q[ ++ tt] = x; + +// 从队头弹出一个数 +hh ++ ; + +// 队头的值 +q[hh]; + +// 判断队列是否为空,如果 hh <= tt,则表示不为空 +if (hh <= tt) +{ + +} +``` + + 2. 循环队列 + +```c++ +// hh 表示队头,tt表示队尾的后一个位置 +int q[N], hh = 0, tt = 0; + +// 向队尾插入一个数 +q[tt ++ ] = x; +if (tt == N) tt = 0; + +// 从队头弹出一个数 +hh ++ ; +if (hh == N) hh = 0; + +// 队头的值 +q[hh]; + +// 判断队列是否为空,如果hh != tt,则表示不为空 +if (hh != tt) +{ + +} +``` + +* 模拟队列 + +```c++ +#include + +using namespace std; + +const int N = 100010; + +int m; +int q[N], hh, tt = -1; + +int main() +{ + cin >> m; + + while (m -- ) + { + string op; + int x; + + cin >> op; + if (op == "push") + { + cin >> x; + q[ ++ tt] = x; + } + else if (op == "pop") hh ++ ; + else if (op == "empty") cout << (hh <= tt ? "NO" : "YES") << endl; + else cout << q[hh] << endl; + } + + return 0; +} +``` + + + + \ No newline at end of file diff --git "a/public/_blogs/AcWing/\351\253\230\347\262\276\345\272\246.md" "b/public/_blogs/AcWing/\351\253\230\347\262\276\345\272\246.md" index 5aa8c34..66c1568 100644 --- "a/public/_blogs/AcWing/\351\253\230\347\262\276\345\272\246.md" +++ "b/public/_blogs/AcWing/\351\253\230\347\262\276\345\272\246.md" @@ -5,21 +5,21 @@ 1. 存储大整数存储在数组中,个位数存在第一位 2. 模拟运算 - + * 高精度加法 - + ```c++ #include #include - + using namespace std; - + const int N = 1e6 + 10; - + vector add(vector &A,vector &B) { vector C; - + int t = 0; for (int i = 0;i < A.size() || i < B.size(); i ++) { @@ -28,7 +28,7 @@ C.push_back(t % 10); t /= 10; } - + if (t) C.push_back(1); return C; } @@ -39,10 +39,71 @@ 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(a[i] - '0'); auto C = add(A,B); //auto 编译器自动推断类型 - + for(int i = C.size() - 1; i >= 0; i--) printf("%d",C[i]); return 0; } ``` - - ​ \ No newline at end of file + + * 高精度减法 + ```c++ + // C = A - B, 满足A >= B, A >= 0, B >= 0 + vector sub(vector &A, vector &B) + { + vector 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; + } + ``` + + * 高精度乘低精度 + ```c++ + // C = A * b, A >= 0, b >= 0 + vector mul(vector &A, int b) + { + vector 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(); + + return C; + } + ``` + + * 高精度除以低精度 + + ```c++ + // A / b = C ... r, A >= 0, b > 0 + vector div(vector &A, int b, int &r) + { + vector 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; + } + ``` + + \ No newline at end of file