【比赛链接】
【题解链接】
【A】Paper Airplanes
【思路要点】
- 按照题意计算即可。
- 时间复杂度\(O(1)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } long long k, n, s, p; int main() { read(k), read(n), read(s), read(p); long long each = n / s + (n % s != 0); long long tot = each * k; writeln(tot / p + (tot % p != 0)); return 0; }
【B】Battleship
【思路要点】
- 按照题意计算每个位置可能包含舰船的方案数,取最大值即可。
- 时间复杂度\(O(N^2K)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m; char s[MAXN][MAXN]; int main() { read(n), read(m); for (int i = 1; i <= n; i++) scanf("\n%s", s[i] + 1); int ansx = 1, ansy = 1, ans = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { if (s[i][j] == '#') continue; int now = 0, cnt = 1; for (int k = i + 1; k <= n && k - i + 1 <= m; k++) if (s[k][j] == '.') cnt++; else break; for (int k = i - 1; k >= 1 && i - k + 1 <= m; k--) if (s[k][j] == '.') cnt++; else break; if (cnt >= m) now += cnt - m + 1; cnt = 1; for (int k = j + 1; k <= n && k - j + 1 <= m; k++) if (s[i][k] == '.') cnt++; else break; for (int k = j - 1; k >= 1 && j - k + 1 <= m; k--) if (s[i][k] == '.') cnt++; else break; if (cnt >= m) now += cnt - m + 1; if (now > ans) { ans = now; ansx = i; ansy = j; } } printf("%d %d\n", ansx, ansy); return 0; }
【C】Greedy Arkady
【思路要点】
- 由题目给出的限制,选取\(x=M\)始终是合法的,计算选取\(x=M\)时的收益。
- 令Arkady获得糖果的次数为\(i\),对于同一个\(i\),显然在满足Arkady确实恰好获得了\(i\)次糖果的前提下,\(x\)取值越大,方案越优。
- 枚举\(i\),计算对于每个\(i\)最大的\(x\)的取值,若此时\(x>M\),则忽略之,否则用其更新答案。
- 由于我们先前计算了取\(x=M\)时的收益,我们并不需要考虑对于每个\(i\),\(x\)不取其最大取值的情况。
- 注意可能的越界问题。
- 时间复杂度\(O(D)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } long long n, k, m, d; int main() { read(n), read(k), read(m), read(d); long long tot = k * m, tmp = n / tot; long long ans = m * tmp; if (n % tot >= m) ans += m; for (int i = 1; i <= d; i++) { long long cnt = 1 + k * (i - 1); if (n / cnt > m) continue; else if (n / cnt == 0) break; long long x = n / cnt; chkmax(ans, x * i); } writeln(ans); return 0; }
【D】Single-use Stones
【思路要点】
- 二分答案,贪心判断可行性。
- 时间复杂度\(O(WLog\sum A_i)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, l, r, a[MAXN]; bool check(int mid) { static int b[MAXN], c[MAXN]; int pos; for (int i = 1; i <= n; i++) c[i] = a[i]; memset(b, 0, sizeof(b)); b[pos = 0] = mid; for (int i = 1; i <= n; i++) { if (i - pos > m) return false; while (c[i] != 0 && pos != i) { int tmp = min(c[i], b[pos]); b[pos] -= tmp; b[i] += tmp; c[i] -= tmp; while (b[pos] == 0) pos++; } } return true; } int main() { read(n), read(m); for (int i = 1; i <= n - 1; i++) read(a[i]), r += a[i]; while (l < r) { int mid = (l + r + 1) / 2; if (check(mid)) l = mid; else r = mid - 1; } writeln(l); return 0; }
【E】Short Code
【思路要点】
- 建立字典树,问题转化成给定一棵树,将一些给定的关键点向根节点移动至深度和最小。
- 记\(dp_i\)为一个数组,表示以\(i\)为根的子树中,子问题的最优解:共有\(dp_{i,j}\)个关键点被安排在深度为\(j\)的节点上。
- 转移分为两种,一种是将子节点的信息按位相加合并,另一种是在某一个没有关键点的位置\(x\)上将一个深度最大的关键点重新安排在\(i\)上,也即删除集合中最大的数,并加入当前节点的深度。
- 朴素地实现上述DP,可以得到\(O(N^2)\)的时间复杂度。
- 用平衡树(std::set)+启发式合并维护DP数组的第二维,时间复杂度降至\(O(NLog^2N)\)。
- 用线段树维护DP数组的第二维,并用线段树合并实现转移,时间复杂度降至\(O(NLogN)\)。
- 用长链剖分维护DP数组的第二维,时间复杂度为\(O(N)\)。
- 以下代码笔者使用的是线段树合并的做法。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXP = 1e7 + 5; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct SegmentTree { struct Node { int lc, rc; int sum, Max; } a[MAXP]; int size, n; void init(int x) { n = x; size = 0; a[0].sum = 0; a[0].Max = -1; } void update(int root) { a[root].Max = -1; a[root].sum = 0; chkmax(a[root].Max, a[a[root].lc].Max); chkmax(a[root].Max, a[a[root].rc].Max); a[root].sum += a[a[root].lc].sum; a[root].sum += a[a[root].rc].sum; } void modify(int &root, int l, int r, int pos, int d) { if (root == 0) root = ++size; if (l == r) { a[root].sum += d; if (a[root].sum) a[root].Max = l; else a[root].Max = -1; return; } int mid = (l + r) / 2; if (mid >= pos) modify(a[root].lc, l, mid, pos, d); else modify(a[root].rc, mid + 1, r, pos, d); update(root); } void modify(int &root, int pos, int d) { modify(root, 0, n, pos, d); } int merge(int x, int y, int l, int r) { if (x == 0 || y == 0) return x + y; if (l == r) { a[x].sum += a[y].sum; if (a[x].sum) a[x].Max = l; else a[x].Max = -1; return x; } int mid = (l + r) / 2; a[x].lc = merge(a[x].lc, a[y].lc, l, mid); a[x].rc = merge(a[x].rc, a[y].rc, mid + 1, r); update(x); return x; } int merge(int x, int y) { return merge(x, y, 0, n); } void trans(int root, int depth) { if (a[root].Max == -1) return; modify(root, a[root].Max, -1); modify(root, depth, 1); } long long getans(int root, int l, int r) { if (root == 0) return 0; if (l == r) return 1ll * a[root].sum * l; int mid = (l + r) / 2; return getans(a[root].lc, l, mid) + getans(a[root].rc, mid + 1, r); } long long getans(int root) { return getans(root, 0, n); } } ST; struct Trie { struct Node { int child[26]; int depth, root; } a[MAXN]; int root, size; void init() { root = 0; a[root].depth = 0; ST.modify(a[root].root, 0, 1); } void insert(char *s) { int len = strlen(s + 1), now = root; for (int i = 1; i <= len; i++) { int tmp = s[i] - 'a'; if (a[now].child[tmp] == 0) { a[now].child[tmp] = ++size; a[size].depth = a[now].depth + 1; } now = a[now].child[tmp]; } ST.modify(a[now].root, a[now].depth, 1); } long long getans() { static int q[MAXN], cnt[MAXN]; for (int i = 0; i <= size; i++) cnt[a[i].depth]++; for (int i = 1; i <= size; i++) cnt[i] += cnt[i - 1]; for (int i = 0; i <= size; i++) q[--cnt[a[i].depth]] = i; for (int i = size; i >= 0; i--) { int tmp = q[i]; bool type = a[tmp].root == 0; for (int j = 0; j < 26; j++) if (a[tmp].child[j]) a[tmp].root = ST.merge(a[tmp].root, a[a[tmp].child[j]].root); if (type) ST.trans(a[tmp].root, a[tmp].depth); } return ST.getans(a[root].root); } } Trie; int n; char s[MAXN]; int main() { read(n); ST.init(MAXN - 1); Trie.init(); for (int i = 1; i <= n; i++) { scanf("%s", s + 1); Trie.insert(s); } writeln(Trie.getans()); return 0; }