首页 > 其他分享 >Codeforces Round 941 (Div. 1) 题解(A-C)

Codeforces Round 941 (Div. 1) 题解(A-C)

时间:2024-04-28 21:22:45浏览次数:27  
标签:back int 题解 cin 941 ans Div now

比赛链接:https://codeforces.com/contest/1965

官解链接:https://codeforces.com/blog/entry/128914

比较手速的一场,C 与 D 之间出现了较大的 gifficulty gap。

所幸 C 题猜得比较快(虽然证明其实比较难),最终 rank 190,performance 2525,成功压线拿下 Grandmaster。

cpchenpi,堂堂上红!

  • 一直到自己的目标达成都没有在比赛中拿下过比较难的题,而是靠着手速带来的高表现分上段,说实话自己有点惭愧。

在题解开始前,最后打一个广告,上红名后我建了一个小群(QQ 902592509),欢迎加入讨论包括并不限于算法竞赛(不限于 Codeforces)、电子音乐、猫鼠手游甚至分享生活等一切话题!

CF1965A. Everything Nim

题意

有 \(n\) 堆石子,第 \(i\) 堆有 \(a_i\) 颗。Alice 和 Bob 两人轮流,Alice 先手。每一轮玩家从所有非空堆中拿走相同数量的石子,不能行动的失败。若两位玩家均使用最优策略,求最终获胜者。

题解

若所有堆均为空,则无法行动,即为必败态。

若最小的一堆石子只有 \(1\) 颗,则只有一种行动方式,即从所有石子堆中同时取出一颗石子。

否则,考虑从所有石子堆中取出一颗石子的新状态为 \(S'\)。

  • 若 \(S'\) 为必败态,则走到 \(S'\) 即可;当前状态 \(S\) 为必胜态。

  • 否则,\(S'\) 的后继状态中必然存在必败态,记为 \(S''\)。我们发现 \(S'\) 的后继状态集合包含在 \(S\) 的后继状态集合中!这样,\(S\) 的后继集合中存在必败态,\(S\) 是必胜态。

故当前状态 \(S\) 必为必胜态。(这是 2023 年 ICPC 网络赛中用到的结论!)

那么只要在最小堆大小为 \(1\) 时模拟,直至走到确认必胜/必败态即可。可以使用差分快速实现。

代码实现

void solve() {
    int n; cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    n = a.size();
    int round = 0;
    for (int i = n - 1; i > 0; i--) a[i] -= a[i - 1];
    reverse(a.begin(), a.end());
    while (a.size() && a.back() == 1) {
        a.pop_back(), round ^= 1;
    }
    if (a.size() == 0) round ^= 1;
    cout << (round ? "Bob" : "Alice") << '\n';
}

CF1965B. Missing Subsequence Sum

题意

给定正整数 \(n \le 10^6\) 和 \(k \le n\)。求一个长度 \(m \le 25\) 的数组 \(a_{1..m}\),使得:

  • \(\forall x \in [1, n]\),若 \(x \ne k\),存在和为 \(x\) 的 \(a\) 的子序列。

  • 不存在和为 \(k\) 的 \(a\) 的子序列。

题解

这题有很多精妙的构造方法,这里给出我的思路:

  • 先构造数列使得 \(a\) 的子序列和恰好覆盖 \(1 \sim k - 1\);可以使用二进制在内的很多方法,但请注意最后一步的边界。

  • 向 \(a\) 中加入 \(k + 1\),此时 \(a\) 的子序列和覆盖 \(1 \sim k - 1\) 以及 \(k + 1 \sim 2k\)。

  • 又回到熟悉的二进制!向 \(a\) 中加入 \(2k, 4k, 8k, \cdots\) 直至 \(a\) 的长度到达 \(24\)。

    • 此时可以确认 \(a\) 的子序列和恰好覆盖了 \(n\) 以内所有模 \(2k\) 不与 \(k\) 同余的数!

    • 最后向 \(a\) 中添加 \(3k\)。容易验证我们得到了满足要求的数组。

      • 我们仍然不能表示出 \(k\)!

      • 但 \(\forall t \ge 1\),我们可以表示出 \(t(2k) + k = 3k + (t-1) (2k)\),也就是填上了之后所有空缺。

代码实现

void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> ans;
    int t = 0;
    while (t < k - 1) {
        ans.push_back(min(t + 1, k - 1 - t));
        t += ans.back();
    }
    ans.push_back(k + 1);
    t = 2 * k;
    while (ans.size() < 24) {
        ans.push_back(t);
        t *= 2;
    }
    ans.push_back(3 * k);
    cout << ans.size() << '\n';
    for (int x : ans) cout << x << ' ';
    cout << '\n';
}

CF1965C. Folding Strip

题意

有一个长度为 \(n\) 的纸条,上面有 \(n\) 个 \(0/1\) 数字。可以在任意两数字之间对其任意翻折。

在满足“所有重叠数字相等”的前提下,翻折后最短长度为多少?

题解

一题比较讨厌的猜猜题。正确答案就是“能折则折”,即在所有相邻相同数字之间翻折。

  • 首先,容易发现这样是可行的(如果你第一时间无法证明,可以从实现中看出证明),且最终的结果一定是 \(0/1\) 间隔串。

  • 如何证明最优?若一个结果串中存在相邻相同字符,则我们可以再调用上面的贪心翻折算法,使其变得更短。

    • 注意我其实略去了“翻折能够合并”的说明。从直觉上这是可行的,可以通过异或的方式将多层折叠展开。

可以使用双指针实现。不过稍微推一推结论,甚至有更简洁的实现方法。

代码实现

void solve() {
    int n; cin >> n;
    string s; cin >> s;
    int now = 0, l = 0, r = 0;
    int dir = 1;
    for (int i = 0, j; i < n; i = j) {
        for (j = i + 1; j < n && s[j] != s[j - 1]; j++);
        now += (j - i) * dir;
        l = min(l, now), r = max(r, now);
        dir *= -1;
    }
    cout << r - l << '\n';
}
void solve() {
    int n; cin >> n;
    string s; cin >> s;
    int now = 0, l = 0, r = 0;
    for (int i = 0; i < n; i++) {
        now += (i & 1) ^ (s[i] - '0') ? 1 : -1;
        l = min(l, now), r = max(r, now);
    }
    cout << r - l << '\n';
}

标签:back,int,题解,cin,941,ans,Div,now
From: https://www.cnblogs.com/cpchenpi/p/-/CF1965-solutions

相关文章

  • Codeforces Round 941 (Div. 2)
    A.CardExchange贪心。如果有某个数出现\(k\)次及以上,则通过操作使其数量变为\(k\),再变为其他出现过的数,则会增加至至少\(k\)个,一直进行如上操作,可以发现数组最终只剩\(k-1\)个数;否则为\(n\)。#include<bits/stdc++.h>usingnamespacestd;#definecctieios::......
  • AtCoder-abc351_d 题解
    原题翻译题意简述给定\(H\timesW\)的网格图,如果一个字符是#,则不能走到该字符上;如果是.,则可以走到该字符上,但如果它周围\(4\)个格子中有#字符,则不能再继续行走了。自由度是指从一个格子出发,能走到不同格子的数量(可以出发多次)。求出所有格子的最大自由度。思路考虑......
  • Codeforces Round 941 (Div. 2) D
    好坐牢的一次div2ABC是速通的,结果cf的pretest太弱了。。然后我这次因为想快点,没有再去好好顺一遍思路,状态又不太好,写了一个好简单的错,结果过了。导致我被hack了,爆掉100分。好烦。主要说说这个D。现在能够从算式的层面上理解了这个做法的正确性,就是把二进制位的数字放进去,然后......
  • 通配符匹配|dfs,hash|题解
    [CQOI2014]通配符匹配题目描述几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个是星号(*),可以匹配0个及以上的任意字符:另一个是问号(?),可以匹配恰好一个任意字符。现在需要你编写一个程序,对于给定的文件名列表和一个包含通配符的......
  • DozerCTF-PWN题解
    这次比赛一共放了4道pwn题,3道栈上的,比较菜,只会做栈1.pwn_fclosefrompwnimport*context(os='linux',arch='amd64',log_level='debug')io=remote('139.196.237.232',32985)#io=process("./pwn")libc=ELF("./libc.so.6&q......
  • abc351g 题解
    这场abcF、G质量堪忧。怎么能扔板子上来呢?板子:P4719【模板】"动态DP"&动态树分治Solution这种每次修改对后面询问有影响,又每次都要输出答案的,离线就基本做不了了,这时候就往动态算法想,其实做过几道ddp的题就看出来这是个板子。由于题目中的式子性质优良,我们很明显可以把......
  • CF1966D Missing Subsequence Sum 题解
    题意:给定\(n(n\le10^6)\)和\(k(k\len)\)。构造一个长度小于等于\(25\)的序列\(a\)满足:1.不存在一个子序列的和为\(k\)。2.对于\(1\lei\len,i\nek\),存在一个子序列的和为\(i\)。看到长度为\(25\),首先肯定会想到二进制。那么我们先构造出一个序列\([2^......
  • ABC351G题解
    考虑动态dp的套路,先树剖一下,令\(son(x)\)为点\(x\)的重儿子。\(g_x=\prod\limits_{u\inC(x)\landu\neqson(x)}f_u\)。于是有\(f_x=f_{son(x)}g_x+a_x\)。于是\(\begin{bmatrix}f_x&1\end{bmatrix}=\begin{bmatrix}f_{son(x)}&1\end{bmatrix}\begin{bmatrix}g_......
  • 【BFS】abc351D Grid and Magnet 题解
    传送门D-GridandMagnet题意:给定h行w列的图表,其中部分位置有磁铁,人物会在磁铁四周((x+1,y),(x-1,y),(x,y+1),(x,y-1))停止,某点的自由度定义为从该点出发最多可到的方块数目可以走重复路前置例题:求连通块大小洛谷P1141思路:由自由度的定义联想到连通块的大小,从而决定用BFS......
  • ABC351_F 题解
    实际上很板。考虑在\(i\)后小于\(val_i\)的数都对答案没贡献,所以我们只需要知道在\(i\)后且大于\(val_i\)的数的和以及有多少个这样的数就可以了。知道了我们要求什么,就可以一眼权值线段树。从后往前扫不断加入数,然后访问对应区间即可,当然因为值域比较大,所以还要离散化......