2024牛客多校第6场
6#
A Cake (A)#
首先假设字符串已经确定,对Oscar而言,由于一份蛋糕可以为空,在两人都尽可能取最大值的情况下,相当于忽略所有空的部分、只根据字符串的某个前缀 \(s'\) 划分蛋糕,按照字符串中0占比最大的前缀平均划分一定是最优的。回到游戏第一步,已知Oscar的目标,Grammy移动时应当让最优 \(s'\) 中0占比尽可能小,而Oscar应使之尽可能大,可用类似树上dp的思路求解。
学习了xht赛时的dfs函数,比我那坨代码好多了······
double dfs(int f, int i, int it, int cnt1, int cnt2, double mx) { if(v[i].size() == 1 && v[i][0].t == f) return mx; double ans = it; for(int j = 0; j < v[i].size(); j++) { int t = v[i][j].t, k = v[i][j].k; if(t == f) continue; int x1 = cnt1 + (1 ^ k), x2 = cnt2 + 1; if(it) { ans = min(ans, dfs(i, t, it ^ 1, x1, x2, max(mx, x1 * 1.0 / x2))); } else { ans = max(ans, dfs(i, t, it ^ 1, x1, x2, max(mx, x1 * 1.0 / x2))); } } return max(ans, mx); }
B.Cake 2 (B)#
找规律题,似乎xcpc的签到偏爱找规律()画图观察每块蛋糕的分布位置可得,\(k \leq n/2\) 时,\(ans=n*k+1\),其中特判 \(k*2=n\) 时 \(ans=n\);\(k > n / 2\) 时同理考虑 \(n-k\) 即可。(听说可以用欧拉定理证明,但我太菜了不会证)
D.Puzzle: Wagiri (D)#
(说起来Wagiri是什么意思呢,好奇)
由无向边的环结构想到边双连通分量,被保留的轮边必须位于某个分量内部;再用类似缩点的思想,将每个边双连通分量当作一个点考虑,被保留的切边恰好将所有点连成一棵树。先对所有轮边计算边双连通分量、再对所有切边计算生成树,最后并查集检验连通性即可。注意边双的代码比缩点多一个对边的特判。
主要代码如下,将不合法边的类型记作-1,便于输出:
for(int i = 1; i <= n; i++) { if(!dfn[i]) tarjan(i, 0); } for(int i = 1; i <= n; i++) { if(r[col[i]]) f[find(i)] = find(r[col[i]]); else r[col[i]] = i; } int cntm = 0; for(int i = 1; i <= m; i++) { int x = e[i].f, y = e[i].t; if(e[i].id) { if(find(x) != find(y)) { f[find(x)] = find(y); cntm++; } else { e[i].id = -1; } } else { if(col[x] != col[y]) { e[i].id = -1; } else cntm++; } } int cnt = 0; for(int i = 1; i <= n; i++) { if(f[i] == i) cnt++; } if(cnt > 1) { printf("NO"); return 0; } printf("YES\n"); printf("%d\n", cntm); for(int i = 1; i <= m; i++) { if(e[i].id >= 0) { printf("%d %d\n", e[i].f, e[i].t); } }
F.Challenge NPC 2 (F)#
记节点 \(i\) 连边数量为 \(ind[i]\),若 \(ind[i] = n - 1\) 时必然无解,因为无法找到补图中的任何一条边、将它连入哈密顿路径中;其他情况则必然有解。
可将森林中的多棵树合并为一棵树考虑,从树的根节点出发,以各节点深度将树“分层”,显然同层或层级差 \(\geq 2\) 的节点间没有连边;先将所有偶数层连接、再将所有奇数层连接,即可构造出一组合法解。为避免不同树合并时出现 \(ind[i] = n - 1\) 情况,应选取每棵树直径的端点依次连接。
注意到层级数 \(x\leq 3\) 时,先偶后奇的构造方案出现问题,因此特判 \(n\leq 3\) 情况,当 \(n\geq 4\) 且不存在 \(ind[i] = n - 1\) 时,连接所有树直径形成的最终直径必然 \(\geq 4\),以直径一端为根节点开始分层,使 \(x = d\geq 4\),该方案成立。
特判,之后不需要再考虑无解情况:
if(n == 2) { if(m) printf("-1\n"); else printf("1 2\n"); continue; } else if(n == 3) { if(m == 2) printf("-1\n"); else { if(v[1].size() == 0) printf("2 1 3\n"); else if(v[2].size() == 0) printf("1 2 3\n"); else printf("1 3 2\n"); } continue; } bool p = 0; for(int i = 1; i <= n; i++) { if(ind[i] == n - 1) { printf("-1\n"); p = 1; break; } } if(p) continue;
dfs常规计算直径,合并后ans分层(似乎ans改叫bfs更贴切来着)
r = o1 = o2 = 0; for(int i = 1; i <= n; i++) { if(!d[i]) { o1 = 0; dfs(0, i); if(!r) r = o1; else v[o2].push_back(o1); o2 = o1, o1 = 0; dfs(0, o2); swap(o1, o2); } } ans(0, r, 1); for(int i = 2; i <= n; i += 2) { if(l[i].size() == 0) break; for(int j = 0; j < l[i].size(); j++) { printf("%d ", l[i][j]); } } for(int i = 1; i <= n; i += 2) { if(l[i].size() == 0) break; for(int j = 0; j < l[i].size(); j++) { printf("%d ", l[i][j]); } }
赛时没想到合并直径,导致分层代码巨大一坨,并且 WA 81pts,哭)
I.Intersecting Intervals (I)#
队友最后十分钟交了I题并AC,太强了QwQ
数据范围 \(mn\leq 10^6\),不能使用 \(m^2\) 一类的算法。考虑dp,行与行之间的dp转移可利用最大值性质做预处理等,目标是将复杂度优化为 \(O(m)\),使整体复杂度 \(mn\) 符合要求。
\(dp[i][j]\) 表示第 \(i\) 行第 \(j\) 个数必须选择时的最大值,可在转移过程中保证区间相交。假设 \(k\leq j\),有 \(dp[i][j] = max(dp[i - 1][k] + sum[k][j] + pre[k - 1] + suf[j + 1]\),其中 \(pre[x],suf[x]\) 分别代表以 \(x\) 结尾/开头的最大前缀/后缀和,可 \(O(m)\) 预处理;若 \(pre[j] > pre[k - 1] + sum[k][j]\),则必有 \(pre[j] + sum[j + 1][j + x] > pre[k - 1] + sum[k][j + x]\),故 \(max(pre[k - 1] + sum[k][j])\) 可在dp遍历转移的过程中动态维护;\(suf[j + 1]\) 则仅与 \(j\) 的取值有关。\(k > j\) 时同理计算即可。
for(int i = 1; i <= n; i++) { ll sum = 0; pre[0] = 0; for(int j = 1; j <= m; j++) { s[j] = s[j - 1] + a[i][j]; if(sum < 0) sum = 0; sum += a[i][j]; //注意每行必须选至少一个数,因此sum小于0时先清零再累加 pre[j] = sum; } sum = 0; suf[m + 1] = 0; for(int j = m; j; j--) { if(sum < 0) sum = 0; sum += a[i][j]; suf[j] = sum; } ll mx = MIN; for(int j = 1; j <= m; j++) { mx = max(mx + a[i][j], dp[i - 1][j] + pre[j]); dp[i][j] = mx + max(0ll, suf[j + 1]); } mx = MIN; for(int j = m; j; j--) { mx = max(mx + a[i][j], dp[i - 1][j] + suf[j]); dp[i][j] = max(dp[i][j], mx + max(0ll, pre[j - 1])); } } ll ans = MIN; for(int j = 1; j <= m; j++) { ans = max(ans, dp[n][j]); } printf("%lld\n", ans);
lzz做这题的时候我还在被F恶心,他讲的思路没听到一点,赛后好奇他怎么写的,然后发现我看不懂他代码(哭得更大声了)
J题输出规则太阴间了所以没补TAT
作者:Aderose_yr
出处:https://www.cnblogs.com/meowqwq/p/18337738
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
喵喵喵)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战