cf 刷题杂记(1)
Codeforces Round 969 (Div. 2)#
之前某篇题解一语成谶掉分了,赶在正式开学前打回蓝名 QwQ
C. Dora and C++ (C)#
卡了很久才想起来裴蜀定理,期间还写了个假做法,不好评价。
由 \(ax + by = k\times gcd(a, b)\) 有解,可以将序列中任意两个数的差减小至 \(gcd(a, b)\) 以下。从相对性角度考虑,令 \(c_i\mod gcd(a, b)\) 后重新排序,则 \(ans = c_n' - c_1'\);再贪心地从首项开始加上 \(gcd\),有 \(ans = min(ans, c_i' + gcd - c_{i + 1})\).
D. Iris and Game on the Tree (D)#
妙妙博弈题,写的比C题还快一点,果然我更擅长猜结论,基础还是太不扎实了
观察发现规律:任意字符串的权重只与其首尾字符有关,首尾字符相同则权重为 \(0\),反之为 \(1\);因此整棵树的权重只与根节点和叶子节点有关。当根节点值确定时,最优操作显然容易判断,而根节点不确定时,设叶子节点中已经确定的值数量为 \(cnt_0,cnt_1\),当二者不相等时,先手先行确定根节点更加有利;而 \(cnt_0 = cnt_1\) 时,若叶子节点剩余问号数量为奇数,确定根节点会导致步数落后,因此双方都会尽可能地先填中间节点的值、将确定根节点的步骤留给对方,考虑中间结点数量的奇偶性即可。
E. Iris and the Tree (E)#
题目要求每次操作后都输出答案,此时动态维护很可能比直接求解更优。树上节点按照dfs顺序分布,每条边一定会被下方节点与某个向上回溯的叶子节点经过、被计算两次。数组 \(c_1,c_2\) 记录经过每条边的两个节点编号,\(len\) 记录每个节点经过的边数,对于所有确定的边,其总贡献为已知边权和 \(sum\) 的两倍;设此时仍然无法确定路径的节点有 \(cur\) 个,应将所有剩余边权算入这 \(cur\) 条路径中,即 \(cur \times (w - sum)\).
dep[1] = 1; len[1] = c1[1] = 0; for(int i = 2; i <= n; i++) { scanf("%d", &f[i]); dep[i] = dep[f[i]] + 1; len[i] = c1[i] = 0; } for(int i = 1; i <= n; i++) { int x = i, y = i + 1; if(y > n) y = 1; while(x != y) { if(dep[x] < dep[y]) swap(x, y); if(c1[x]) c2[x] = i; else c1[x] = i; x = f[x], len[i]++; } } ll sum = 0, cur = n; for(int i = 1; i < n; i++) { int x; ll y; scanf("%d%lld", &x, &y); sum += y; len[c1[x]]--, len[c2[x]]--; // len = 0 即该节点路径全部确定 if(len[c1[x]] == 0) cur--; if(len[c2[x]] == 0) cur--; printf("%lld ", sum * 2 + cur * (w - sum)); }
F. Eri and Expanded Sets (F)#
考虑最终集合 \(a\) 中的元素,对顺序排列形成的 \(a\) 数组取差分 \(d\),由奇偶规则得 \(d_i\) 为奇数;对于相邻的 \(d_i\) 与 \(d_{i + 1}\),若 \(d_i\neq d_{i + 1}\),可以产生新的元素 \(a_i' = (a_i + a_{i + 2}) / 2\),且 \(a_i' \neq a_{i + 1}\),因此 \(d_i = d_{i + 1}\) 一定成立,即最终 \(a\) 为等差数列。而 \(a_i' = (a_i + a_{i + 2}) / 2\) 时,新产生 \(d_i' = |d_i - d_{i + 1}|\),由裴蜀定理可得,等差数列的公差为原数组差分后、各元素gcd的最大奇数因子。因此对于一个合法的子序列,其差分数组 \(gcd \{d_i\} = 2^x\),且 \(l\) 至 \(r\) 的范围扩大时,合法子序列的gcd只减不增、不可能变成非法,可以用st表维护区间gcd求解。
int a[N], eq[N]; int st[N][20]; int query(int l, int r) { int k = __lg(r - l + 1); // 希望我不要再写挂st表(闭目)(祈祷) return __gcd(st[l][k], st[r - (1 << k) + 1][k]); } void solve() { int n; scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); } if(n == 1) { printf("1\n"); return; } n--; // 方便考虑差分数组 for(int i = 1; i <= n; i++) { st[i][0] = abs(a[i + 1] - a[i]); } for(int k = 1; k <= __lg(n); k++) { for(int i = 1; i + (1 << k) - 1 <= n; i++) { st[i][k] = __gcd(st[i][k - 1], st[i + (1 << k - 1)][k - 1]); } } eq[n + 1] = n + 1; for(int i = n; i; i--) { if(a[i] == a[i + 1]) eq[i] = eq[i + 1]; else eq[i] = i; } int l = 1, r = 1; ll ans = 1; // 最后一项 for(int i = 1; i <= n; i++) { l = max(l, eq[i]); r = max(r, l); // 二进制中只有一个1,即为2的x次幂 while(r <= n && __builtin_popcount(query(l, r)) > 1) r++; // i~r 至 i~n 满足,i~i 至 i~eq[i] 满足 ans += n - r + 1 + eq[i] - i + 1; } printf("%lld\n", ans); }
Educational Codeforces Round 169 (Rated for Div. 2)#
E. Not a Nim Problem (E)#
sg函数打表题,将每堆石子视为一个单独的游戏,计算其sg函数,发现其分布具有规律性。具体而言,所有偶数的sg函数为0,奇数的sg函数与其最小质因数相等;除2以外,第 \(i\) 个质数的sg函数等于 \(i\);特别的,1的sg函数为1. 可在线性筛素数的同时维护sg函数值。对于整个序列,其sg函数为每堆石子sg函数的异或和。(这个规律应该也能证,但证明好麻烦不写了)
Codeforces Round 965 (Div. 2)#
C. Perform Operations to Maximize Score (C)#
又是中位数的题,非常好没写出来,不知道在干什么。为了使最终权值最大,将 \(k\) 全部用于改变最大值或改变中位数是最优的,若同时改变,则权值变化量不会额外增大。改变最大值的操作较为简单,对于改变中位数的操作,可以二分答案求出最大可能的中位数。
“对于一个序列直接求中位数需要排序,不便于寻找最优解,而对于一个确定的数,有相对容易的方法验证其是否为中位数。考虑二分答案。”(自己写的东西都不记得,不好评价)
D. Determine Winning Islands in Race (D)#
代码不难写,但思维很妙。由于Bessie每次只能由岛屿 \(i\) 到达岛屿 \(i + 1\),且Bessie先行动,Elsie为了获胜,必须提前到达Bessie前方的岛屿。预处理出Elsie到达每个岛的最短时间 \(d_i\),Bessie获胜时应对所有 \(i > s\) 满足 \(d_i \geq i - s\),即 \(s \geq max(i - d_i)\). 而在 \(d_i\) 时间内,一部分岛屿会被Bessie破坏、Elsie无法通过,因此从 \(n - 1\) 至 \(1\) 递推计算,multiset维护 \(i - d_i\) 的最大值,递推过程中从集合中动态删除Bessie历经岛屿所影响的 \(i - d_i\).
while(m--) { int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); r[y].push_back(x); // 同时保存反边,便于倒序遍历时操作 } bfs(); // 边权为1的最短路 multiset <int> s; for(int i = n; i; i--) { for(auto t : r[i]) { int mx = i - d[t] - 1; s.insert(mx); e[t].push_back(mx); // 保存t节点会影响的(i - d[i])值 } for(auto t : e[i]) { s.erase(s.find(t)); // 经过i时删除i影响的值 } if(i < n) { int x = -1; if(s.size()) { auto it = s.end(); it--; x = *it; } if(i >= x) ans[i] = 1; else ans[i] = 0; } }
Educational Codeforces Round 168 (Rated for Div. 2)#
E. Level Up (E)#
好一道抽象的题目)由于 \(k\) 增大时等级增长变慢,两者为线性关系,对于任意 \(i\) 都存在一个确定的值 \(k_i\),\(x\geq k_i\) 时,第 \(i\) 个怪物会与主角战斗,否则其将逃跑;二分答案预处理出 \(k_1\) 至 \(k_n\),之后可 \(O(1)\) 查询。对所有 \(k\) 值建立线段树,下标 \(l\) 位置保存 \(k = l\) 时已经与主角发生过战斗的怪物数量,从 \(1\) 至 \(n\) 递推计算,每次二分过程中单点查询 \(num_k\),满足 \(num_k / k + 1 \leq a[i]\) 的 \(k\) 为合法值;求出 \(k_i\) 后,对于所有 \(k\geq k_i\),主角会与怪物战斗,区间修改 \(k_i\) 至 \(n\) 即可。
现在我应该不会再打错线段树板子了,所以没放线段树代码
b[1] = 1; modify(1, 1, n, 1, n, 1); // 初始化 for(int i = 2; i <= n; i++) { int l = 1, r = n; while(l <= r) { int mid = (l + r) / 2; int x = query(1, 1, n, mid); if(x / mid + 1 <= a[i]) { b[i] = mid; r = mid - 1; } else l = mid + 1; } modify(1, 1, n, b[i], n, 1); }
作者:Aderose_yr
出处:https://www.cnblogs.com/meowqwq/p/18395317
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
喵喵喵)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现