当前位置:首页 > 技术 > 正文内容

Codeforces 比赛复盘与题解分析

访客 技术 2026年6月26日 1
实际考试时间为11月5日,因当天有Codeforces比赛,次日状态不佳。 总分:100 + 50 + 0 + 35 = 230 分,校内排名第二。[点击查看链接]
题目 T1:组合计数优化 本题考察对组合恒等式的理解与动态规划优化能力。 核心思路是枚举0的个数,通过组合数递推关系 $ C_n^i = C_{n-1}^i + C_{n-1}^{i-1} $ 推导出状态转移方程。 定义: - $ f_i $:长度为 $ i $ 且包含偶数个0的方案数; - $ g_i $:长度为 $ i $ 且包含奇数个0的方案数。 初始值:$ f_1 = m, g_1 = 1 $ 转移方程: $$ \begin{cases} f_i = m \cdot g_{i-1} + f_{i-1} \\ g_i = m \cdot f_{i-1} + g_{i-1} \end{cases} $$ 该递推可表示为矩阵形式,使用矩阵快速幂可在 $ O(\log n) $ 时间完成状态转移,整体复杂度 $ O(d^3 \log n) $,其中 $ d=2 $。 进一步数学推导可得闭式解: $$ \text{ans} = \frac{(m+1)^n + (m-1)^n}{2} $$ 此公式可通过单位根反演或生成函数方法证明。 #include #define int long long using namespace std; const int mod = 998244353; int n, m; struct Matrix { int a[2][2]; Matrix operator*(const Matrix& y) const { Matrix z; for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j) z.a[i][j] = (a[i][0] * y.a[0][j] + a[i][1] * y.a[1][j]) % mod; return z; } void set(int a00, int a01, int a10, int a11) { a[0][0] = a00; a[0][1] = a01; a[1][0] = a10; a[1][1] = a11; } } ans, base; signed main() { cin >> n >> m; m %= mod; --n; ans.set(m, 1, 0, 0); base.set(m, 1, 1, m); while (n) { if (n & 1) ans = ans * base; base = base * base; n >>= 1; } cout << ans.a[0][0]; }
题目 T2:数值稳定性与乱搞策略 本题涉及高精度数值计算,正解需使用自然对数将乘积转为求和,并结合泰勒展开截断前100项近似。 关键思想: - 对每个值取 $ \ln(1 - x) $,在区间 $ (0, 1) $ 内可用泰勒级数逼近; - 维护加法结果后还原指数; - 由于精度限制,仅保留前 $ d=100 $ 项即可满足要求。 时间复杂度约为 $ O(q \cdot d \cdot \log n) $,常数较大。 然而,数据设计存在明显缺陷: - $ O(qn) $ 的暴力做法仍可通过部分测试点; - 部分分设置不合理,导致大量选手通过随机化或启发式剪枝通过; - 甚至存在构造数据可使常见"乱搞"算法崩溃。 因此,一种可行的投机策略是: - 利用性质:当 $ x < 0.991 $ 时,经过约1300次迭代后其影响趋于零; - 使用线段树维护更新次数,若某节点更新超过阈值(如1300)则停止传播; - 在随机数据下表现良好,但易被构造数据卡死。 该方法在开启编译优化(O2)时可通过,但在某些环境下会因浮点精度问题失败。 #include using namespace std; const int MAXN = 1e5 + 10; int n, q, opt, l, r, B; double x; struct tree { double val; int cnt; } t[MAXN << 2]; void build(int l, int r, int rt) { if (l == r) { cin >> t[rt].val; t[rt].val = 1 - t[rt].val; return; } int m = (l + r) >> 1; build(l, m, rt << 1); build(m + 1, r, rt << 1 | 1); t[rt].val = t[rt << 1].val * t[rt << 1 | 1].val; } double query(int l, int r, int L, int R, int rt) { if (L <= l && r <= R) return t[rt].val; int m = (l + r) >> 1; double res = 1.0; if (L <= m) res *= query(l, m, L, R, rt << 1); if (m < R) res *= query(m + 1, r, L, R, rt << 1 | 1); return res; } void update(int l, int r, int L, int R, int rt) { if (t[rt].cnt > B) return; if (l == r) { t[rt].val = t[rt].val * x + (1 - x); ++t[rt].cnt; return; } int m = (l + r) >> 1; if (L <= m) update(l, m, L, R, rt << 1); if (m < R) update(m + 1, r, L, R, rt << 1 | 1); t[rt].val = t[rt << 1].val * t[rt << 1 | 1].val; t[rt].cnt = min(t[rt << 1].cnt, t[rt << 1 | 1].cnt); } signed main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> q; build(1, n, 1); B = q; if (n * q > 100000000) B = 1300; while (q--) { cin >> opt >> l >> r; if (opt == 0) { printf("%.6lf\n", query(1, n, l, r, 1)); } else { cin >> x; if (x == 1) continue; update(1, n, l, r, 1); } } }
题目 T3:动态规划状态设计难题 未掌握核心状态转移方式,未能写出正确解法,暂无补题记录。
题目 T4:树上深度贪心结论题 观察到答案具有结构性特征:对于一棵树,设 $ s_i $ 表示深度大于等于 $ i $ 的节点数量,则最优答案为: $$ \max_{1 \le i \le \text{maxdep}} \left( i + \left\lceil \frac{s_{i+1}}{k} \right\rceil \right) $$ **解释逻辑**: - 深度 $ i $ 以内的点可以在当前层处理完毕; - 剩余的 $ s_{i+1} $ 个节点必须按 $ k $ 个一组分配,每组至少消耗一层; - 当 $ i $ 太小,$ \left\lceil \frac{s_{i+1}}{k} \right\rceil $ 被浪费; - 当 $ i $ 太大,$ i $ 本身贡献过大而 $ s_{i+1} $ 过小,不划算。 因此,最大值一定出现在某个平衡点。 **实现方法**: - 离线处理所有询问; - 将询问按 $ k $ 排序; - 使用双端队列维护凸包,利用斜率单调性进行决策点维护; - 类似于斜率优化中的"单调队列"技巧。 最终复杂度:$ O(n + q \log q) $,瓶颈在于排序。 #include using namespace std; const int MAXN = 1e6 + 10; int n, T, a[MAXN], d[MAXN], cnt[MAXN], mxdep; int head = 1, tail, q[MAXN], ans[MAXN]; struct ask { int k, id; bool operator<(const ask& other) const { return k < other.k; } } t[MAXN]; long double slope(int x, int y) { return 1.0 * (cnt[x + 1] - cnt[y + 1]) / (x - y); } signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> T; ++cnt[1]; d[1] = 1; for (int i = 1; i <= T; ++i) { cin >> a[i]; t[i] = {a[i], i}; } sort(t + 1, t + T + 1); for (int i = 2; i <= n; ++i) { cin >> a[i]; d[i] = d[a[i]] + 1; ++cnt[d[i]]; mxdep = max(mxdep, d[i]); } for (int i = mxdep - 1; i >= 1; --i) cnt[i] += cnt[i + 1]; for (int i = 1; i <= mxdep; ++i) { while (head < tail && slope(q[tail - 1], q[tail]) < slope(q[tail - 1], i)) --tail; q[++tail] = i; } for (int i = 1; i <= T; ++i) { while (head < tail && slope(q[head], q[head + 1]) > -t[i].k) ++head; ans[t[i].id] = q[head] + ceil(1.0 * cnt[q[head] + 1] / t[i].k); } for (int i = 1; i <= T; ++i) cout << ans[i] << " "; }

相关文章

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。