Codeforces Round 1023 (Div. 2)


A. LRC and VIP

题意:求能不能把\(a\)数组分成两部分,使得它们的\(gcd\)不相同。

考虑\(gcd\)是小于等于这些数的,那么我们可以把最大值和非最大值分开。无解的情况就是所有数相同。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int max = *std::max_element(a.begin(), a.end());
    if (std::ranges::count(a, max) == n) {
    	std::cout << "NO\n";
    } else {
    	std::cout << "YES\n";
    	for (int i = 0; i < n; ++ i) {
    		if (a[i] == max) {
    			std::cout << 2 << " \n"[i == n - 1];
    		} else {
    			std::cout << 1 << " \n"[i == n - 1];
    		}
    	}
    }
}

B. Apples in Boxes

题意:\(n\)个数,两个人博弈。每轮一个人选择使得一个\(a_i\)减一。如果操作后\(max - min\)大于\(k\)则数。如果所有\(a_i\)都是零那么则无法操作,无法操作的人输。求第一个人能不能赢。

为了保证极差小于等于\(k\)。减最小值肯定不优。那么不减最小值就只能减其它数,随着其它数变小,极差也只会变小。所以如果一开始第一个人不输,则会一直到所有数都变成零才结束,此时如果总和是奇数第一个人就赢。一开始就输的情况就是最大值减一后极差大于\(k\),或者最大值有多个且极差大于\(k\)

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int max = *std::max_element(a.begin(), a.end());
    int min = *std::min_element(a.begin(), a.end());
    int cnt = std::ranges::count(a, max);
    i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
    if (max - 1 - min > k || (max - min > k && cnt > 1) || sum % 2 == 0) {
    	std::cout << "Jerry\n";
    } else {
    	std::cout << "Tom\n";
    }
}

C. Maximum Subarray Sum

题意:给你数组\(a\),有些地方没填。你需要填上后使得最大子段和等于\(k\)

\(inf = 1e18\)
先把没填的地方填上\(-inf\)。然后如果数组的最大子段和大于\(k\),那么无解。
否则我们可以通过填一个地方,其它保持是\(-inf\)来达成目标。如果\(i\)没填,上一个没填的地方是\(pre\),下一个填的地方是\(suf\)。那么我们填了\(i\)后,可能会更改\([pre+ 1, suf - 1]\)的最大子段和。发现随着我们填的数越大,最大子段和是单调不减的,那么可以二分最小的填上后最大子段和大于等于\(k\)的值。最后检查是不是等于\(k\),是就找到了解。记得特判全部都填的情况下最大子段和是不是等于\(k\)

点击查看代码
void solve() {
    int n;
    i64 k;
    std::cin >> n >> k;
    std::string s;
    std::cin >> s;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    const i64 inf = 1e18;
    for (int i = 0; i < n; ++ i) {
    	if (s[i] == '0') {
    		a[i] = -inf;
    	}
    }

    std::vector<int> suf(n);
    suf[n - 1] = n;
    for (int i = n - 2; i >= 0; -- i) {
    	suf[i] = suf[i + 1];
    	if (s[i + 1] == '0') {
    		suf[i] = i + 1;
    	}
    }

    auto check = [&](int l, int r) -> i64 {
    	i64 max = 0, sum = 0;
    	for (int i = l; i <= r; ++ i) {
    		sum = std::max(0ll, sum + a[i]);
    		max = std::max(max, sum);
    	}

    	return max;
    };	

    if (check(0, n - 1) > k) {
    	std::cout << "NO\n";
    	return;
    }

    for (int i = 0, pre = -1; i < n; ++ i) {
    	if (s[i] == '0') {
    		int l = pre + 1, r = suf[i] - 1;
    		i64 lo = -inf, hi = inf;
    		while (lo < hi) {
    			i64 mid = lo + hi >> 1ll;
    			a[i] = mid;
    			if (check(l, r) >= k) {
    				hi = mid;
    			} else {
    				lo = mid + 1;
    			}
    		} 

    		a[i] = lo;
    		if (check(l, r) == k) {
    			std::cout << "YES\n";
    			for (int i = 0; i < n; ++ i) {
    				std::cout << a[i] << " \n"[i == n - 1];
    			}
				return;
    		}
    		a[i] = -inf;
    		pre = i;
    	}
    }

    if (check(0, n - 1) != k) {
    	std::cout << "NO\n";
    } else {
    	std::cout << "YES\n";
    	for (int i = 0; i < n; ++ i) {
    		std::cout << a[i] << " \n"[i == n - 1];
    	}
    }
}

D. Apple Tree Traversing

题意:给你一棵树。每次选择一条路径,路径端点为\(u, v\)。这条路径不能包含选择过的点。如果把\(cnt, u, v\)三个整数加入答案,\(cnt\)是路径上的点数,然后把路径上的点标记为选择过。求字典序最大的答案。

赛时思路正确。但调了一个小时没调出来,代码写的复杂把自己弄红温了。
我们应该先选择一条树的直径,如果有多条直径,选择一个端点最大的。那么把直径删除后把树分成了一棵森林,这些树互不影响,递归用同样的做法做就行。
细节就是每次把这棵子树的点存下来,不要遍历所有点,否则会超时。
时间复杂度大概是\(O(n\sqrt{n})\),类似长链剖分。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::vector<int>> adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

	std::vector<int> d(n), st(n), fa(n, -1);
    std::vector<std::array<int, 3>> vec;
    auto work = [&](auto & self, int root) -> void {
    	std::vector<int> a;
    	auto dfs = [&](auto & self, int u) -> void {
    		a.push_back(u);
    		for (auto & v : adj[u]) {
    			if (st[v] || fa[u] == v) {
    				continue;
    			}
    			fa[v] = u;
    			d[v] = d[u] + 1;
    			self(self, v);
    		}
    	};

    	d[root] = 0;
    	fa[root] = -1;
    	dfs(dfs, root);
    	for (auto & x : a) {
    		if (d[x] > d[root] || (d[x] == d[root] && x > root)) {
    			root = x;
    		}
    	}

    	a.clear();
    	d[root] = 0;
    	fa[root] = -1;
    	dfs(dfs, root);
    	int u = root;
    	for (auto & x : a) {
    		if (d[x] > d[u] || (d[x] == d[u] && x > u)) {
    			u = x;
    		}
    	}

    	vec.push_back(std::array<int, 3>{d[u] + 1, std::max(root, u), std::min(root, u)});
    	for (int i = u; u != -1; u = fa[u]) {
    		st[u] = 1;
    	}
    	for (auto & x : a) {
    		if (!st[x]) {
    			self(self, x);
    		}
    	}
    };

    work(work, 0);
    std::ranges::sort(vec, std::greater<>());
    std::vector<int> ans;
    for (auto & [x, y, z] : vec) {
    	ans.push_back(x);
    	ans.push_back(y + 1);
    	ans.push_back(z + 1);
    }
    int m = ans.size();
    for (int i = 0; i < m; ++ i) {
    	std::cout << ans[i] << " \n"[i == m - 1];
    }
}
posted @ 2025-05-06 01:55  maburb  阅读(806)  评论(3)    收藏  举报