2024牛客多校7&8
7#
A Maximum Subarray Sum (A)#
(出题人解法没看明白)
解法2的切入点类似之前某场div2的D题(题解传送门),将操作过程视为选出 \(\lfloor n/k\rfloor\) 个长度为 \(k\) 的子序列,答案序列中大于 \(k\) 的部分暂时与舍弃部分等价,即最后舍弃的数字下标满足 \(i' = i\space mod\space k\). 设 \(dp[i][x][y][p]\) 表示到 \(i\) 位置,换走 \(x\) 个数、换入 \(y\) 个数,当前选择状态为 \(z\)(选或不选)时的最大和,每个计入答案的数可能存在按序选入/中途换入两种选择方式,同理未计入答案的数也存在直接抛弃/换出两种可能。当前数被选中时,由于子序列长度至少为 \(k\),为避免出现间断点,\(dp[i][x][y][1]\) 只能由 \(dp[i - k + 1][x][y][1]\)(长度为 \(k\) 的子序列)或 \(dp[i - 1][x][y][1]\)(答案序列中大于 \(k\) 的部分)转移而来,前者需要从 \(0\) 到 \(m\) 枚举换出数字的数量,其他所有情况则可以 \(O(1)\) 转移。利用前缀和与set之类的容器处理换出后的序列最大值,整体复杂度可优化至 \(nm^3\) 左右。特别地,当 \(i\space mod\space k = k - 1\) 时,由于舍弃的数不可能超过 \(k - 1\) 个,该位置上的数字不能被直接抛弃,只进入 \(0\) 至 \(m\) 的循环、讨论可能存在的换出情况。代码还是借鉴其他队答案才写明白的,所以没必要放这里了)
I Fight Against the Monster (I)#
题解思路是二分答案,不过不用二分也行。\(h\leq m\) 时显然答案为 \(h\),对于 \(h > m\) 的情况,若生产出的 \(k\) 台新机器仍不足以将怪物杀死,可以选择直接补充与剩余血量相等的机器数目(\(h'\leq m - k\) 时),或补充 \(m - k\) 台机器至恰好足以生产新机器,依此思路可 \(O(1)\) 计算答案,代码如下:
if(h <= m) printf("%lld\n", h); else if(m <= k) printf("%lld\n", m); else { ll sum = h - m; if(sum <= k) printf("%lld\n", m); else { ll cnt = sum / m, x = sum % m; if(x >= k) printf("%lld\n", m + cnt * (m - k) + (x - k)); else printf("%lld\n", m + cnt * (m - k)); } }
8#
上周四不在家所以没打,后来自己写的时候各种卡题,难过)
A Haitang and Game (A)#
事实上A题并不是一道博弈题,最终序列中加入的所有 \(d\) 是固定的,不存在任何可能改变胜负的博弈策略。具体而言,若序列中 \(d\)(本身不存在于序列中)的倍数有 \(a_i,a_j,...\),且 \(gcd(a_i,a_j,...) = d\),则 \(d\) 必然会被加入。数据范围 \(a_i\leq 10^5\),遍历 \(1\) 至 \(a_{max}\),枚举倍数判断其是否为合法的 \(d\),由调和级数得整体复杂度 \(nloga_{max}\).
E Haitang and Math (E)#
由 \(m\leq n\leq 10^{12}\) 得 \(s(m)\leq 108\),对 \([n - 108, n]\) 枚举因数即可。直接 \(T*100\sqrt n\) 似乎过不了,题解使用区间筛可优化至 \(T(\frac{\sqrt n}{log\space n} + d(n)log\space n)\),但不幸的是我不会区间筛,也没看懂队友的模板,数学题真就只能靠队友了吗TAT
J Haitang and Triangle (J)#
可爱构造题,首先考虑无解,\(1\) 在任何情况下都不能与其他数组成三角形,合法子序列的总数不可能超过 \(n - 3\) 个,当 \(m = n - 2\) 时,答案不存在,特判无解。当 \(x > 1\) 时,从 \(x\) 到 \(x + k\) 的顺序序列必然存在 \(k - 2\) 个合法的子序列,而不合法子序列的构成则相对复杂,因此优先考虑对不合法部分的构造。当 \(a + b\leq c\) 时 \(a,b,c\) 不能构成三角形,为构造尽可能多的此类组合,应尽可能选择排列中最小/最大的数。
由此可得构造策略:假设 \(n - m - 2 = 3k\),先保留前 \(2k\) 与后 \(k\) 个数,取出中间 \(m + 2\) 个数,顺序排列形成 \(m\) 个合法子序列;将前 \(2k\) 与后 \(k\) 个数依次组合,诸如 \(1,2k,x,2,2k - 1,x + 1,3,...\) 相邻三个数中,较小数之和只可能为 \(2k + 1\) 或 \(2k + 2\),由于 \(m + 2\geq 2\),必有 \(x > 2k + 2\),即任何子序列必然不合法。该部分以 \(1\) 开头、位于顺序排列的 \(m + 2\) 个数后,\(1\) 的特殊性质可以确保合法子序列不会增加。对于 \((n - m - 2)\mod 3 = 2\),相当于前 \(k\) 个数中额外加入两个,同理构造即可;\((n - m - 2)\mod 3 = 1\) 时,先假设 \(m' = m - 1\) ,构造余数为 \(2\) 的情况;再交换后部分中 \(1\) 与 \(2k\) 的位置,此时后部分性质保持不变,由前部分顺序排列可知,相邻两数之差 \(1 < 2k\),\(2k\) 必然能够与 \(x - 2, x - 1\) 形成新的合法子序列。
主要代码:
if(m == n - 2) { printf("-1\n"); return; } int num = m + 2, las = n - (m + 2); int pre = las / 3 * 2, suf = las / 3; bool flag = 0; if(las % 3 == 1) { num--; las++; flag = 1; } if(las % 3 == 2) { pre += 2; } for(int i = pre + 1; i + suf <= n; i++) { ans[i - pre] = i; } for(int i = num + 1; i <= n; i += 3) { int x = (i - num) / 3; ans[i] = x + 1; ans[i + 1] = pre + 1 - ans[i]; ans[i + 2] = x + pre + n - las + 1; if(x == 0 && flag) swap(ans[i], ans[i + 1]); } for(int i = 1; i <= n; i++) { printf("%d ", ans[i]); }
另有:这题写完交上去两次都WA,一度怀疑我的构造假了(关键题解还不是这么构造的,更害怕了),无奈求助队友才发现我代码的一个弱智错误。。。不好评价,并且再次感谢xht orz
作者:Aderose_yr
出处:https://www.cnblogs.com/meowqwq/p/18353431
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
喵喵喵)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现