2024杭电多校第6场
6#
1004 不醒人室 (hdu7497)#
模拟题,思路和题解略有不同,由于数据保证各时间段有序,可在循环外设变量 \(awake\) 表示在当前循环位置、最后清醒的时间,遍历上课时间的同时用计划的睡觉时间更新 \(awake\) 即可,时间复杂度为线性。
主要代码:
ll awake = 0; int j = 1; for(int i = 1; i <= n; i++) { ll l = c[i].l, r = c[i].r; //c[i]课程时间,s[i]睡觉时间 if(awake < r) { if(awake >= l) awake = -1; for(; j <= m; j++) { ll st = s[j].r, ed = s[j].r + (s[j].r - s[j].l) * 2; if(j < m) ed = min(ed, s[j + 1].l); if(st <= l && ed >= r) { awake = ed; break; } else if(st > l) { awake = -1; break; } } if(awake < 0) break; } }
至于为什么思路讲得有点抽象,因为本来就没啥思路······这场杭电的前一天晚上打cf去了(参见上一篇题解),赛时脑子还不太清醒,一通乱写过了wyq的数据就交,没想到对了······ wyq评价:最符合题意的一集)
1005 交通管控 (hdu7498)#
由 \(k\) 的数据范围想到状压dp,红绿灯三种颜色状态需要三进制状压。对于相同的操作杆组合,每根操作杆的使用顺序不影响最终结果,因此用类似背包的方式求解,使用滚动数组可优化空间复杂度至 \(3^{10}\) 左右。答案对 \(m\) 取模,由于最终答案可能是 \(m\) 的整数倍,取模后为 \(0\),无法区分是否出现该组合,需要另开一个bool数组表示该状态的合法性。
以下为dp代码,需注意初始化:
for(int s = 0; s < p[k]; s++) { dp[s][0] = dp[s][1] = 0; vis[s][0] = vis[s][1] = 0; } dp[0][0] = 1; vis[0][0] = 1; for(int i = 1; i <= n; i++) { int ii = i & 1; for(int s = 0; s < p[k]; s++) { dp[s][ii] = 0; vis[s][ii] = 0; } string c; cin >> c; for(int s = 0; s < p[k]; s++) { if(!vis[s][ii ^ 1]) continue; vis[s][ii] = 1; dp[s][ii] += dp[s][ii ^ 1]; dp[s][ii] %= mo; int to = 0; for(int z = 0; z < k; z++) { int cur = (s / p[z]) % 3; if(c[z] == '+') cur = (cur + 1) % 3; else if(c[z] == '-') cur = (cur + 2) % 3; to += p[z] * cur; } vis[to][ii] = 1; dp[to][ii] += dp[s][ii ^ 1]; dp[to][ii] %= mo; } }
除此以外我还尝试过分层图+拓扑的方法,逻辑严不严谨另说,由于空间无法优化,喜提MLE
1007 树上 \(MEX\) 问题 (hdu7500)#
赛时没想到如何统计最终答案,看了眼题解似乎能动态维护,于是按照之前的想法继续写,还真可以)
直接统计子图的 \(MEX\) 值非常麻烦,可从每个子图对答案的“贡献”角度思考。若某一子图中包含权值为 \(0\) 至 \(x - 1\) 的所有点,可确定其 \(MEX\geq x\),即该子图的贡献不小于 \(x\). 先假设所有子图 \(MEX = 0\),对于包含 \(0\) 权值的 \(t\) 个子图,其贡献至少为 \(1\),故应补充答案,有 \(sum\) \(+\)\(=\) \(t\);在此基础上,对于同时包含 \(0,1\) 权值的子图,同理有 \(sum\) \(+\)\(=\) \(t'\);······ 以此类推,若每种子图的数量可快速计算,即可 \(O(n)\) 统计答案。
树形dp可 \(O(n)\) 求出以每个节点为根节点的子图数量,递推即乘法原理 \(cnt[i] = \prod (cnt[t] + 1)\). 对于包含 \(0\) 至 \(x - 1\) 所有节点的子图数量 \(ans\),考虑动态维护,每次加入新节点时计算 \(ans\) 的变化量即可。以 \(0\) 权值点为根,前 \(x - 1\) 个节点形成的最小子图为 \(s\),若 \(x\) 权值在 \(s\) 内部,所有包含 \(s\) 的子图必然包含 \(x\),\(ans\) 不变;若 \(x\) 在 \(s\) 外,由于 \(s\) 内部所有点必然与根节点 \(0\) 权值处连接,\(x\) 不可能是它们的祖先,因此顺着 \(x\) 的父亲节点向上寻找,即可将 \(x\) 加入 \(s\) 中。如此处理后,\(x\) 至其祖先的链上答案发生变化,其他位置仍然遵循乘法原理,设该链上子图总数 \(k\),答案变化 \(ans = (ans/k) * k'\);\(x\) 向上寻找父节点 \(f\),有 \(k'=k'*cnt[f]/(cnt[x] + 1)\),原先数量除去所有不选择 \(x\) 的情况即可。由于每个点只被加入 \(s\) 一次,复杂度仍然为 \(O(n)\).
sum = cnt[mp[0]]; in[mp[0]] = 1; //in数组表示点是否在s内,由于每次操作的s固定,不需要用并查集 ll ans = cnt[mp[0]]; for(int i = 1; i < n; i++) { int x = mp[i]; if(in[x] == 0) { in[x] = 1; ll tim = cnt[x]; //即原文中k' int pre = x; for(int j = f[x]; ; j = f[j]) { if(in[j]) { ans = ans * tim % mo * inv(cnt[pre] + 1) % mo; //cnt[pre] + 1即原文中k sum += ans; if(sum >= mo) sum -= mo; break; } tim = tim * cnt[j] % mo * inv(cnt[pre] + 1) % mo; in[j] = 1; pre = j; } } else { sum += ans; if(sum >= mo) sum -= mo; } } printf("%lld\n", sum);
作者:Aderose_yr
出处:https://www.cnblogs.com/meowqwq/p/18347251
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
喵喵喵)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现