开出了五题。
C. Distributing Candies
注意到如果 \(n\) 是奇数一定无解,\(n\) 是偶数可以分成两个 \(\frac n2\)。
写完这个题之后去吃饭了,浪费 1H。
void work() {int n; cin >> n;if (n & 1) return cout << "No" << endl, void();cout << "Yes" << endl;cout << n / 2 << ' ' << n / 2 << endl;
}
K. Xiangqi
注意到状态数只有 \((10\times 9)^2\times 2\) 个,所以直接爆搜。
const int MAXN = 11;
int ans[MAXN][MAXN][MAXN][MAXN][2]; // 1=chess 2=knight 3=draw
int inst[MAXN][MAXN][MAXN][MAXN][2]; // is in stackconst int kn_dx[] = {2, 2, -2, -2, 1, 1, -1, -1};
const int kn_dy[] = {1, -1, 1, -1, 2, -2, 2, -2};
const int kh_dx[] = {1, 1, -1, -1, 0, 0, 0, 0};
const int kh_dy[] = {0, 0, 0, 0, 1, -1, 1, -1};int solve(int kx, int ky, int cx, int cy, int now) {if (ans[kx][ky][cx][cy][now]) return ans[kx][ky][cx][cy][now];if (inst[kx][ky][cx][cy][now]) return 3;inst[kx][ky][cx][cy][now] = 1;auto ret = [&](int x) {inst[kx][ky][cx][cy][now] = 0;return ans[kx][ky][cx][cy][now] = x;};if (now == 0) { // chessif (kx == cx || ky == cy) return ret(1);bool win = false, draw = false;for (int i = 1; i <= 9; ++i) {if (i == cx) continue;int tmp = solve(kx, ky, i, cy, !now);if (tmp == 1) win = true;if (tmp == 3) draw = true;}for (int i = 1; i <= 10; ++i) {if (i == cy) continue;int tmp = solve(kx, ky, cx, i, !now);if (tmp == 1) win = true;if (tmp == 3) draw = true;}if (win) return ret(1);if (draw) return ret(3);return ret(2);} else {bool win = false, draw = false;for (int i = 0; i < 8; ++i) {if (cx == kx + kh_dx[i] && cy == ky + kh_dy[i])continue;int x = kx + kn_dx[i], y = ky + kn_dy[i];if (x < 1 || x > 9 || y < 1 || y > 10) continue;if (x == cx && y == cy) return ret(2);int tmp = solve(x, y, cx, cy, !now);if (tmp == 2) win = true;if (tmp == 3) draw = true;}if (win) return ret(2);if (draw) return ret(3);return ret(1);}
}void work() {int kx, ky, cx, cy; cin >> kx >> ky >> cx >> cy;int t = solve(kx, ky, cx, cy, 1);if (t == 1) return cout << "YES" << endl, void();cout << "NO" << endl;
}
F. Bitwise And Path
朴素的想法是开 \(2^{12}\) 个并查集,第 \(i\) 个并查集记录当只有满足 \(w\&i=i\) 的边时,图的连通情况。查询的时候就是从大到小贪心每一位选。
但是这个显然过不了,因为暴力的维护加边每次最多会遍历 \(2^{12}\) 个子集。但是我们可以剪枝,如果一个集合 \(T\) 已经连通了,那么所有 \(T\) 的子集 \(K\subseteq T\) 肯定也都连通了。赛时不会证这个复杂度,还以为是卡过去的。没想到正解就是这个。
const int MAXV = 1 << 12, MAXN = 1e3 + 5;
int fa[MAXV][MAXN], n, q;int find(int i, int x) {if (fa[i][x] == x) return x;else return fa[i][x] = find(i, fa[i][x]);
}void dfs(int u, int v, int S, int b) {int fu = find(S, u), fv = find(S, v);if (fu == fv) return;fa[S][fu] = fv;for (int i = b; i < 12; ++i) {if (!(S >> i & 1)) continue;dfs(u, v, S ^ (1ll << i), i + 1);}
}void work() {cin >> n >> q;for (int i = 0; i < MAXV; ++i)for (int j = 1; j <= n; ++j)fa[i][j] = j;long long anss = 0;while (q--) {char op; cin >> op;if (op == '+') {int u, v, w; cin >> u >> v >> w;dfs(u, v, w, 0);int fu = find(0, u), fv = find(0, v);if (fu != fv) fa[0][fu] = fv; } else {int u, v; cin >> u >> v;if (find(0, u) != find(0, v)) {anss--; continue;}int ans = 0;for (int i = 11; i >= 0; --i) {int tmp = (ans | (1 << i));int fu = find(tmp, u), fv = find(tmp, v);if (fu == fv) ans = tmp;}// cout << ans << endl;anss += ans;}}cout << anss << endl;
}
M. Many Convex Polygons
队友开出来的多项式。膜拜。
G. Bucket Bonanza
首先注意到我们肯定不会合并三个及以上的水桶,因为这肯定不优。
然后注意到,我们合并的顺序一定是 \(V\) 最大的与 \(L\) 最小的合并,\(V\) 第二大的和 \(L\) 第二小的合并,证明可以用交换论证。
然后你把 \(V\) 和 \(L\) 分别排序,做一个前缀和,之后二分贡献最大到哪里就做完了。
const int MAXN = 2e5 + 5;
int n, q, v[MAXN], l[MAXN], smv[MAXN], sml[MAXN];void work() {cin >> n;for (int i = 1; i <= n; ++i)cin >> v[i];for (int i = 1; i <= n; ++i)cin >> l[i];v[0] = LLONG_MAX;v[n + 1] = INT_MIN;sort(v + 1, v + 1 + n, greater<int>());sort(l + 1, l + 1 + n);for (int i = 1; i <= n; ++i) {sml[i] = sml[i - 1] + l[i];smv[i] = smv[i - 1] + v[i];}cin >> q;while (q--) {int t; cin >> t;int L = 0, R = n + 1;while (L + 1 < R) {int mid = (L + R) >> 1;if (v[mid] > t * l[mid]) L = mid;else R = mid;}cout << smv[L] - t * sml[L] << ' ';}cout << endl;
}
I. Chifan
唉,chifan 也应该接一个的。还是上半场吃饭吃多了。