题目传送门
官方题解传送门
A .操作序列
sol:如果STL熟悉,那么就是一道模拟题。就是输入有点奇葩。但是看了官方题解中提到了平衡树。嗯,没错,map和set的底层都是平衡树。红黑树不会,平衡树觉得还是fhq-treap好敲。所以,提供一份map的解法和一份fhq-treap的解法。
- map
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; map<int, int> mp; int main() { int t; scanf("%d", &t); while (t--) { int n, m, op; char c; scanf("%d%c", &n, &c); if (c == ' ') { scanf("%d", &m); bool ok = true; for (int i = n - 30; i <= n + 30; i++) if (mp.count(i)) ok = false; if (ok) mp[n] = m; continue; } if (n == -1) { if (mp.empty()) puts("skipped"); else { printf("%d ", mp.begin()->second); mp.erase(mp.begin()); } } else { if (mp.count(n)) printf("%d ", mp[n]); else puts("0"); } } return 0; }
- fhq-treap
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 1e6 + 10; struct Treap { int key, val; int rand; int lson, rson; } node[MAXN]; int root, tot; int new_node(int n, int k) { int i = ++tot; node[i].key = n; node[i].val = k; node[i].rand = rand(); node[i].lson = node[i].rson = 0; return i; } void split(int rt, int& a, int& b, int k) { if (rt == 0) { a = b = 0; return; } if (node[rt].key <= k) { a = rt; split(node[rt].rson, node[a].rson, b, k); } else { b = rt; split(node[rt].lson, a, node[b].lson, k); } } void merge(int& rt, int a, int b) { if (a == 0 || b == 0) { rt = a + b; return; } if (node[a].rand < node[b].rand) { rt = a; merge(node[rt].rson, node[a].rson, b); } else { rt = b; merge(node[rt].lson, a, node[b].lson); } } void insert(int n, int k) { int a, b, c; split(root, root, c, n + 30); split(root, a, b, n - 30 - 1); if (b == 0) b = new_node(n, k); merge(root, a, b); merge(root, root, c); } int pop(int& point) { if (node[point].lson == 0) { int tmp = node[point].val; point = node[point].rson; return tmp; } return pop(node[point].lson); } int get_val(int n) { int a, b, c; split(root, root, c, n); split(root, a, b, n - 1); int tmp; if (b == 0) tmp = 0; else tmp = node[b].val; merge(root, a, b); merge(root, root, c); return tmp; } int main() { int t; scanf("%d", &t); while (t--) { int n, k; char c; scanf("%d%c", &n, &c); if (c == ' ') { scanf("%d", &k); insert(n, k); } else if (n == -1) { if (root == 0) puts("skipped"); else printf("%d ", pop(root)); } else { printf("%d ", get_val(n)); } } return 0; }
fhq-treap真的是平衡树里最简单的了,这么久没敲还能凭对代码的记忆而不是代码的理解一次敲对。
B .树上子链
sol:和求树的直径差不多,树的直径是求树上最长的链,这题转化成求权值最大的链。
- 树的直径
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const LL INF = 0x3f3f3f3f3f3f3f3f; const int MAXN = 1e5 + 10; vector<int> edge[MAXN]; int val[MAXN]; LL res = -INF; LL dfs(int u, int f) { LL max1 = 0, max2 = 0; for (int v : edge[u]) { if (v == f) continue; LL val = dfs(v, u); if (val > max2) max2 = val; if (max2 > max1) swap(max1, max2); } res = max(res, max1 + max2 + val[u]); return max1 + val[u] > 0 ? max1 + val[u] : 0; } int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &val[i]); for (int i = 2; i <= n; i++) { int u, v; scanf("%d%d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } dfs(1, -1); printf("%lld ", res); return 0; }
C .交换游戏
sol:看了题解后感觉是一个挺裸的记忆化搜索。补掉了。
- 记忆化搜索
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 1 << 12 | 10; int dp[MAXN]; int read() { int n = 0; char c = getchar(); while (c != '-' && c != 'o') c = getchar(); while (c == '-' || c == 'o') { n = n << 1 | (c == 'o'); c = getchar(); } return n; } int dfs(int n) { if (dp[n] != -1) return dp[n]; int tmp = n, cnt = 0; while (tmp) { cnt ++; tmp -= tmp & -tmp; } dp[n] = cnt; int a = 1, b = 2, c = 4; for (int i = 1; i <= 10; i++) { if ((n & a) && (n & b) && !(n & c)) { dp[n] = min(dp[n], dfs(n ^ a ^ b ^ c)); } if (!(n & a) && (n & b) && (n & c)) { dp[n] = min(dp[n], dfs(n ^ a ^ b ^ c)); } a <<= 1, b <<= 1, c <<= 1; } return dp[n]; } int main() { memset(dp, -1, sizeof(dp)); int t; scanf("%d", &t); while (t--) { int n = read(); printf("%d ", dfs(n)); } return 0; }
D .收集纸片
sol:比赛的时候想着爆搜整张地图然后代码也没实现出来,题目中的纸片最多只有10张,所以突破口在纸片,可以枚举所以的收集顺序。那么枚举的方法就有很多了。官方题解中用了dfs的方法,那么我这里就用next_premutation这个函数来解决了。这个函数在蓝桥杯中也被多次考到。
- 全排列
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int INF = 0x3f3f3f3f; PII p[20]; int order[20]; int main() { int t; scanf("%d", &t); while (t--) { scanf("%*d%*d%d%d", &p[0].first, &p[0].second); int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d%d", &p[i].first, &p[i].second); order[i] = i; } order[n + 1] = 0; int res = INF; do { int sum = 0; for (int i = 1; i <= n + 1; i++) { sum += abs(p[order[i]].first - p[order[i - 1]].first); sum += abs(p[order[i]].second - p[order[i - 1]].second); } if (sum < res) res = sum; } while (next_permutation(order + 1, order + 1 + n)); printf("The shortest path has length %d ", res); } return 0; }
原来给出的地图大小是没用的
E .方块涂色
sol:总共$n$行,被涂了$r$行,还剩$(n - r)$行。总共$m$列,被涂了$c$列,还剩$(m - c)$列,所以答案就是$(n - r) * (m - c)$。注意到结果可能爆int就不会错了。
- 数学
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; int main() { int n, m, r, c; while (~scanf("%d%d%d%d", &n, &m, &r, &c)) printf("%lld ", 1LL * (n - r) * (m - c)); return 0; }
F .累乘数字
sol:听说有人拿到题就直接用python高精度上了。也是一种不错的选择,容易抢一血。不过更高效的方法就是输出$n$之后再输出$d$次$00$。
- python大数
while True : try : n, m = map(int, input().split()) print(n * 100 ** m) except : break
- 数学
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; int main() { int n, m; while (~scanf("%d%d", &n, &m)) { printf("%d", n); for (int i = 1; i <= m; i++) printf("00"); puts(""); } return 0; }
G .仓库选址
sol:一种比较直白的方式是枚举每个位置做仓库,然后算出这个位置的花费,然后找出最小值。复杂度是$O((n * m) * (n * m))$。官方题解就是这种方法,参照官方题解。但是其实可以把行和列分开考虑使复杂度变成$O((n * m) + (n * m))$。
- 思维
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL, LL> PII; const LL INF = 0x3f3f3f3f; const LL MAXN = 110; LL row[MAXN], col[MAXN]; LL cal(LL* arr, LL len) { LL res = INF; for (LL i = 1; i <= len; i++) { LL sum = 0; for (LL j = 1; j <= len; j++) sum += abs(i - j) * arr[j]; res = min(res, sum); } return res; } int main() { LL t; scanf("%lld", &t); while (t--) { LL n, m, k; scanf("%lld%lld", &m, &n); memset(row, 0, sizeof(row)); memset(col, 0, sizeof(col)); for (LL i = 1; i <= n; i++) { for (LL j = 1; j <= m; j++) { scanf("%lld", &k); row[i] += k; col[j] += k; } } printf("%lld ", cal(row, n) + cal(col, m)); } return 0; }
H .货物种类
sol:关键是差分,然后我是用map来维护的
- 差分
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 1e5 + 10; map<int, int> pre[MAXN], now; int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int l, r, d; scanf("%d%d%d", &l, &r, &d); pre[l][d] ++; pre[r + 1][d] --; } int res, cnt = 0; for (int i = 1; i <= n; i++) { for (auto it : pre[i]) { now[it.first] += it.second; if (now[it.first] == 0) now.erase(it.first); } if (now.size() > cnt) { cnt = now.size(); res = i; } } printf("%d ", res); return 0; }
I .工具人
J .计算A + B
sol:skipped的情况有'+'在最前面,'+'在最后面,'+'的次数不为1。剩下的就是高精度了。反正我是用python水过去的,python大数和split真香。至于C++,之后再补上吧。
- python大数
t = int(input()) for i in range(t) : n = input().split('+') if len(n) != 2 or n[0] == '' or n[1] == '': print('skipped'); else : print(int(n[0]) + int(n[1]))
ps:一血的代码太精华了,佩服佩服,贴个传送门。
--------------------------------------------------前来填坑--------------------------------------------------
- 高精度模拟
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 10010; struct BigInt { const static int mod = 10000; const static int dlen = 4; int a[600], len; BigInt() { memset(a, 0, sizeof(a)); len = 1; } BigInt(const char* s) { memset(a, 0, sizeof(a)); int l = strlen(s); len = 0; for (int i = l - 1; i >= 0; i -= dlen) { int tmp = 0, start = max(0, i - 4 + 1); for (int j = start; j <= i; j++) tmp = tmp * 10 + (s[j] ^ '0'); a[len ++] = tmp; } } friend BigInt operator + (BigInt a, BigInt b) { BigInt res; res.len = max(a.len, b.len); int tmp = 0; for (int i = 0; i < res.len; i++) { tmp += a.a[i] + b.a[i]; res.a[i] = tmp % mod; tmp /= mod; } if (tmp) res.a[res.len ++] = tmp; return res; } void output() { printf("%d", a[len - 1]); for (int i = len - 2; i >= 0; i--) printf("%04d", a[i]); puts(""); } }; char s[MAXN]; int find_plus(const char* s) { int res = -1; for (int i = 0; s[i]; i++) { if (s[i] == '+') { if (res != -1) return -1; else res = i; } } return res; } int main() { int t; scanf("%d", &t); while (t--) { scanf("%s", s); int index = find_plus(s); if (index == -1 || index == 0 || s[index + 1] == '