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 也应该接一个的。还是上半场吃饭吃多了。

posted @ 2025-11-17 17:42  小蛐蛐awa  阅读(6)  评论(0)    收藏  举报