ABC337

1|0基本情况

ABC 秒了,D 数组在空间复杂度上面第一次疯狂吃亏,吃了两次罚时过。

赛后看官方题解,发现C做法薄纱我。

2|0C - Lining Up 2

https://atcoder.jp/contests/abc337/tasks/abc337_c

这题一眼链表,我用双向链表实现,代码石山。

官方题解就题论题,更本质。

其实这题并没必要开双向链表,因为实际上只有一种位置关系,左到右。

直接维护一个类似单链表的数据结构就行了。

#include <iostream> #include <vector> int main() { using namespace std; unsigned N; cin >> N; vector<unsigned> B(N + 1, N + 1); // B[i] 存储下标对应的人的右边的人 unsigned front;// 队头 for(unsigned i = 1; i <= N; ++i){ int A; cin >> A; if(A < 0) front = i;//维护队头 else B[A] = i; //更新A右边的人 } while(front <= N){ // 就类似邻接表遍历,从队头开始 cout << front << " "; front = B[front]; } cout << endl; return 0; }

3|0E - Bad Juice

https://atcoder.jp/contests/abc337/tasks/abc337_e

看似是交互,实则是结合bitmask的构造题。

因为每个人是否拉肚子就 0,1 两种情况,所以可以按位构造。

对于第 i 个人,让他喝完编号第 i1 的所有果汁。

如果第 i 个人中毒了,那么第 i 位为 1 的果汁肯定有问题,这里每个 i 中毒所表示的信息是不互相冲突的,可以叠加的:

如果第 3,4,6,7 个人中毒了,那么就说明有问题的果汁其 3,4,6,7 位都为 1

这样只要最多拉 m,(n2m) 个人就能涵盖所有状况,可以证明拉的人最少。

把所有 i 中毒的位全部或上去,答案就是中毒的果汁。

signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n; std::cin >> n; int m = 0; while ((1 << m) < n) {m += 1;} std::cout << m << std::endl; for (int i = 0; i < m; i++) {//选n个人品尝 std::vector<int> res; for (int j = 0; j < n; j++) if (j & (1 << i)) {//如果j果汁第i位是1,就让i品尝 res.push_back(j + 1); } std::cout << sz(res) << ' '; for (auto& x : res) {std::cout << x << ' ';} std::cout << std::endl; } std::string s; std::cin >> s; int ans = 0; for (int i = 0; i < m; i++) if (s[i] == '1') { ans |= (1 << i);//有毒的果汁第i位肯定为1 } std::cout << (ans + 1) << std::endl; return 0; }

4|0F - Usual Color Ball Problems

https://atcoder.jp/contests/abc337/tasks/abc337_f

超级恶心的滑动窗口。

为了方便起见,我们考虑序列

C=(C1,C2,,C2N)\coloneqq(C1,C2,,CN,C1,C2,,CN),

这个序列是通过连接两个给定序列的副本得到的,并且将此问题看作是要找到针对 C 的段 [l,l+N1] 的操作中盒子中的球的总数,对于每个 l=1,2,,N。令 f(c,l,r) 表示段 [l,r] 中颜色为 c 的球的数量,g(c) 表示整个原始序列 C 中颜色为 c 的球的数量。

首先,我们考虑如何找到固定段 [l,l+N1] 的答案。作为针对段 [l,l+N1] 的操作的结果,如果用于存储颜色为 c 的球的箱子数量为 b(c,l),那么结果中颜色为 c 的球的数量是 min{b(c,l)×K,g(c)},因此答案是

ansl=c=1Nmin{b(c,l)×K,g(c)}.

一旦我们找到每种颜色 cb(c,l),我们也可以找到 ansl,因此我们接下来考虑如何找到它。

如果一个球被放入一个空箱子中(这增加了用于该颜色的箱子的数量),如果这个球是要处理的颜色的第 1 个、(K+1) 个、(2K+1) 个、(3K+1) 个等球,则会消耗一个空箱子。因此,让我们称每种颜色的第 1 个、(K+1) 个、(2K+1) 个、(3K+1) 个等球为机会球

每次处理一个机会球(任何颜色的)时,都会消耗一个空箱子,因此 b(c,l) 等于前 M 个要处理的机会球中颜色为 c 的球的数量。因此,要处理的第 M 个机会球的位置是什么?

当从位置 l 开始操作时,段 [l,r] 中颜色为 c 的机会球的数量是 f(c,l,r)/K,因此段 [l,r] 中的机会球(任何颜色的)的数量是

S(l,r)\coloneqqc=1Nf(c,l,r)K.

因此,要处理的第 M 个机会球的位置是使得 S(l,r)M 的最小 r。记为 rl,则有 b(c,l)=f(c,l,rl)/K,因此所求的答案表示为

(1)ansl=c=1Nmin{f(c,l,rl)K×K,g(c)}.

(如果没有 rl+N1 满足 S(l,r)M,则为了方便起见,我们让 rl\coloneqql+N1。)因此,为了找到 ansl,只需要为固定的 l 找到 rl,并评估上述式子 (1)。然而,简单地对所有 l=1,2,,N 进行计算是不可能在执行时间限制内完成的。

相反,注意到根据上面的讨论,r1r2rN。为了按顺序评估 l=1,2,,N 的答案,rl 可以以滑动窗口的方式高效地找到,而滑动窗口时,还要保持对当前段 [l,r] 对应的值 (1),并且每次 lr 增加一个时应用增量更新,以便以总共 O(N) 的时间找到 ansl

signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, m, k; std::cin >> n >> m >> k; std::vector<int> c(n * 2); for (int i = 0; i < n; i++) {std::cin >> c[i]; --c[i]; c[i + n] = c[i];}//破环成链 std::vector<int> all(n);//维护该颜色在1~n的出现次数 for (int i = 0; i < n; i++) {all[c[i]] += 1;} std::vector<int> f(n);//即f(c, l, r),[l, r]区间内颜色为c的球的出现数量 std::vector<int> box(n);//box[c]表示颜色为c的盒子的数量 i64 ans = 0, totBox = 0; auto add = [&](int color, int x) { //消除没加之前的改颜色球对答案的影响 ans -= std::min(box[color] * k, all[color]); totBox -= box[color]; box[color] -= (f[color] + k - 1) / k; f[color] += x;//[l, r]区间该颜色球的数量+1 //处理加之后的影响 box[color] += (f[color] + k - 1) / k;//用的盒子是总数 / k向上取整 totBox += box[color];//盒子数量加上去 ans += std::min(box[color] * k, all[color]);//更新答案 }; for (int l = 0, r = 0; l < n; l++) { while (r < l + n and totBox < m) {add(c[r], 1); r++;}//如果盒子还没用完并且右端点也还没到头 std::cout << ans << '\n'; add(c[l], -1);//剪掉开头 } return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18207182.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示