Codeforces Round 926 (Div. 2) 题解
比赛链接:https://codeforces.com/contest/1929
官解链接:https://codeforces.com/blog/entry/125943
出的很差的一场。
UPD1:加入了 E 题使用或卷积的另解。
推歌
CF1929A. Sasha and the Beautiful Array
任意排列数组
题解
见过最显然的 A 题,奠定了本场题目简单的基调。
众所周知
代码实现
void solve() { int n; cin >> n; vector<int> a(n); for (int &x: a) cin >> x; cout << ranges::max(a) - ranges::min(a) << '\n'; }
CF1929B. Sasha and the Drawing
在一个
题解
垃圾场特有的猜猜题,但我一开始画图画错了,导致猜错了……
在一个最优方案中,涂色一个格子只会使染色对角线的数目增加
我们有一种染色方案,能使得共

最后两次操作分别染色右上角和右下角的两个小方格。
要证明这是最优方案,只要说明不可能使所有操作都染色两条对角线。很简单:左下角和右上角的两个小方格,它们所在的两条对角线均只能通过这一条小方格染色,也就是这两次涂色必然进行;但它们又在同一条反对角线上,因此两次涂色操作必定至少有一次只染色一条对角线。
若所有操作均染色两条对角线,需要的操作次数为
代码实现
void solve() { int n, k; cin >> n >> k; if (k <= 4 * n - 4) { cout << (k + 1) / 2 << '\n'; } else { cout << k + 2 - 2 * n << '\n'; } }
CF1929F. Sasha and the Wedding Binary Search Tree
给定一棵 BST,其中部分点的权值不定
题解
非常简单的题,甚至完全可以放到 C 题之前……分数虚高是因为被放在最后,而很多人被 D 挡住了,根本没来得及看后两题。
BST 等价于中序遍历是非降序列。对每段连续的
使用
代码实现
void solve() { int n, m; cin >> n >> m; vector<int> l(n + 1), r(n + 1), val(n + 1); for (int i = 1; i <= n; i++) { cin >> l[i] >> r[i] >> val[i]; } vector<int> a; a.push_back(1); function<void(int)> dfs = [&](int u) { if (l[u] != -1) dfs(l[u]); a.push_back(val[u]); if (r[u] != -1) dfs(r[u]); }; dfs(1); a.push_back(m); Z ans = 1; auto comb = [&](int n, int m) -> Z { Z res = 1; for (int i = 1; i <= m; i++) { res = res * (n + 1 - i) / i; } return res; }; for (int i = 1, j; i <= n; i = j) { if (a[i] != -1) { j = i + 1; } else { for (j = i; a[j] == -1; j++); int len = j - i; int num = a[j] - a[i - 1] + 1; ans *= comb(len + num - 1, len); } } cout << ans << '\n'; }
CF1929C. Sasha and the Casino
赔率为
题解
这场比赛最有趣的一题。大家在学概率论时,也许听说过这么一个问题:
若赔率为
- 第
次下注 元(以保证一旦赢钱,总计能赚 元钱),且一旦第一次赢钱就停手。
是否代表赌徒一定稳赚不赔?事实上当胜率
回到问题本身来。加入了至多连续输
-
每次赢钱后的局面没有本质区别。因此,策略只与当前连续输的局数有关。
-
策略需要确保无论输多少(
)次之后赢,至少能赚一元钱。 -
由于本金是有限的,在这个前提下,花的本金越少越好。
因此这个策略确实是最优的。花的本金的最大值为连续输了
由于金额是指数增长的,时间复杂度为
代码实现
void solve() { int k, x, a; cin >> k >> x >> a; int need = 1; for (int round = 0; round < x && need <= a; round++) { need += need / (k - 1) + 1; } cout << (need <= a ? "YES" : "NO") << '\n'; }
CF1929D. Sasha and a Walk in the City
有一棵树,求满足以下条件点集的数量:
- 树上任意一条简单路径至多包含点集中的两个点。
题解
分析满足条件点集的性质,发现其等价于存在一个点,以它为根时,任意节点之间不存在父子关系。在任意定根的树上,它等价于上面两种情况之一:
-
任意两点之间不存在父子关系;
-
或存在一个点是其它所有节点的父亲,但不是它们的 LCA(即是它们 LCA 的祖先)。
考虑使用树形 DP 如何计算。记
对第一种情况,若选择
对第二种情况,我们必须选择
代码实现
void solve() { int n; cin >> n; vector<vector<int>> adj(n); for (int e = 1; e < n; e++) { int u, v; cin >> u >> v; u--, v--; adj[u].push_back(v); adj[v].push_back(u); } vector<Z> dp(n); Z ans = 0; function<void(int, int)> dfs = [&](int u, int fa) { dp[u] = 1; for (int v : adj[u]) { if (v == fa) continue; dfs(v, u); dp[u] *= dp[v]; ans += dp[v] - 1; } dp[u] += 1; }; dfs(0, 0); cout << ans + dp[0] << '\n'; }
CF1929E. Sasha and the Happy Tree Cutting
有一棵树,树上有
题解
考虑将一条边涂色能影响哪些路径,可以发现不同影响的总数是
我们也可以做更细致的分析:对路径
之后即可朴素地状压 DP。可以看出这是一个 DAG 上最短路的模型,使用类似最短路的写法。总时间复杂度
代码实现
constexpr int INF = 1e9 + 7; void solve() { int n; cin >> n; vector<vector<int>> adj(n); for (int e = 1; e < n; e++) { int u, v; cin >> u >> v; u--, v--; adj[u].push_back(v); adj[v].push_back(u); } int k; cin >> k; vector<int> emsk(n); for (int p = 0; p < k; p++) { int u, v; cin >> u >> v; u--, v--; emsk[u] ^= 1 << p; emsk[v] ^= 1 << p; } vector<int> buc; function<void(int, int)> dfs = [&](int u, int fa) { for (int v : adj[u]) { if (v == fa) continue; dfs(v, u); emsk[u] ^= emsk[v]; } for (int v : adj[u]) { if (v != fa && (~emsk[u] & emsk[v])) { buc.push_back(emsk[v]); } } }; dfs(0, 0); vector<int> dp(1 << k, INF); dp[0] = 0; for (int msk = 0; msk < (1 << k) - 1; msk++) { for (int trans : buc) { dp[msk | trans] = min(dp[msk | trans], dp[msk] + 1); } } cout << dp[(1 << k) - 1] << '\n'; }
UPD:另解
事实上没有边的种类为
考虑令
注意对一般的问题,中间结果可能会溢出。但由于我们只关心方案数是否为 int32
范围内。
代码实现
using Z = int; vector<Z> fwtOr(const vector<Z> &f, int n, Z mul = 1) { vector<Z> res = f; for (int k2 = 2, k = 1; k2 <= n; k2 <<= 1, k <<= 1) { for (int i = 0; i < n; i += k2) { for (int j = 0; j < k; j++) { res[i + j + k] += res[i + j] * mul; } } } return res; } void solve() { // use the same emsk vector<Z> g(1 << k, 0); for (int x : emsk) { g[x] = 1; } vector<Z> conv(g); int ans = 1; g = fwtOr(g, 1 << k); while (!conv[(1 << k) - 1]) { conv = fwtOr(conv, 1 << k); for (int i = 0; i < (1 << k); i++) { conv[i] *= g[i]; } conv = fwtOr(conv, 1 << k, -1); for (int &x : conv) { if (x) { x = 1; } } ans++; } cout << ans << '\n'; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?