UCUP Season4 Stage5 Nanjing 赛后总结
开出了五题。
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 stack
const 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) { // chess
if (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 也应该接一个的。还是上半场吃饭吃多了。

浙公网安备 33010602011771号