首页 > 其他分享 >CF 教育场 135 题解

CF 教育场 135 题解

时间:2022-09-24 14:12:26浏览次数:79  
标签:10 int 题解 CF dfs else leq 135 SG

比赛链接

A题 Colored Balls: Revisited(签到)

给定 \(n\) 种颜色的球,其中颜色 \(i\) 的球的数量是 \(cnt_i\),保证 \(\sum\limits_{i=1}^n cnt_i\) 是奇数。

在一次操作中,我们可以选择两种不同颜色的球,然后各拿走一个。我们不断进行该操作,直到全场只剩下一个球,问这个球的颜色可能是什么?(如果有多种可能,输出其中一种即可)

\(T\leq 10^3,n\leq 20,1\leq cnt_i\leq 100\)

我写的时候是认为,数量最多的更容易剩下来,直接输出数量最多的那种颜色即可,现在小小证明一下(其实也很简单):按照从小到大排个序,然后依次消掉当前剩下来数量最小的数量即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 30;
int n, a[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    int p = 1;
    for (int i = 2; i <= n; ++i)
        if (a[p] < a[i]) p = i;
    return p;
}
int main() {
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

B题 Best Permutation(构造)

给定一个排列 \(\{a_n\}\),现在执行如下函数:

int solve(int n, int *a) {
 int x = 0;
 for (int i = 1; i <= n; ++i)
     if (x < a[i]) x += a[i];
 	else x = 0;
	return x;
}

给定 \(n\),要求我们构造一个排列,使得返回的值尽可能大,并输出该排列。

\(T\leq 97,4\leq n\leq 100\)

假定最后一个数是 \(x\),那么得到的返回值不会超过 \(2x-1\):如果前面 \(n-1\) 个值得到的数是 \(y\),那么

  1. \(y<x\),得 \(y+x\leq (x-1)+x=2x-1\)
  2. \(y>x\),最后的值直接变成 0

那么显然,最后一个数应该填 \(n\),倒数第二个填 \(n-1\),随后让前 \(n-2\) 个数得到的值变成 0 即可:

  1. 随机法:倒数第三个填 1,然后前面 \(n-3\) 个数全部随机(得到正数的概率要远大于得到 0 吧,然后直接被 1 压成 0 了)
  2. 构造法:如果 \(n-2\) 是偶数,那么直接类似 \(2,1,4,3,\cdots,n-2,n-3\) 即可;是奇数,就直接 \(1,2,3,5,4,7,6,\cdots,n-2,n-3\)
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, a[N];
void solve() {
    cin >> n;
    a[n] = n, a[n - 1] = n - 1;
    if (n % 2 == 0) {
        for (int i = 1; i < n - 1; i += 2) a[i] = i + 1, a[i + 1] = i;
    }
    else {
        a[1] = 1, a[2] = 2, a[3] = 3;
        for (int i = 4; i < n - 1; i += 2) a[i] = i + 1, a[i + 1] = i;
    }
    //
    for (int i = 1; i <= n; ++i) cout << a[i] << " ";
    cout << endl;
}
int main() {
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}

C题 Digital Logarithm(数据结构,贪心)

我们可以对一个数 \(x\) 进行操作 \(f(x)\),而 \(f(x)\) 函数返回 \(x\) 的十进制位数。

给定两个长度为 \(n\) 的数列 \(\{a_n\},\{b_n\}\),我们可以选定某个数列的某个数,并执行操作 \(f(x)\),问至少需要执行多少次操作,才能使得两个数列“相同”?(数列排序后完全相同,就可以视为“相同”)

\(T\leq 10^4,n,\sum n\leq 2*10^5,1\leq a_i,b_i\leq 10^9\)

有相同的数就先过滤掉,保证剩下来的数没有交集(这部分直接用 map 啥的统计一下,整体 \(O(n)\) 的)。

注意到 \(a_i,b_i\leq 10^9\),这意味着所有数操作一次后都变成了个位数,所以我们先对两个数列里面大于等于 10 的都操作一下,随后再过滤一遍,之后对所有大于 1 的且剩下来的个位数操作一次即可。

整体复杂度:第一次过滤交集的复杂度是 \(O(n\log n)\)(我用的 map,如果手写哈希或者 unordered_map 可以变成 \(O(n)\)),后面操作的流程就是 \(O(n+10)\) 了。

#include<bits/stdc++.h>
using namespace std;
int f(int x) {
    int tot = 0;
    for (; x; x /= 10) ++tot;
    return tot;
}
const int N = 200010;
int n, a[N], b[N], c[N];
int solve() {
    //read
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) cin >> b[i];
    memset(c, 0, sizeof(int) * (n + 1));
    //solve
    sort(a + 1, a + n + 1);
    sort(b + 1, b + n + 1);
    int tot = 0;
    map<int, int> vis1;
    for (int i = 1; i <= n; ++i) ++vis1[a[i]];
    for (int i = 1; i <= n; ++i)
        if (vis1.find(b[i]) != vis1.end()) {
            c[i] = 1;
            if (--vis1[b[i]] == 0)
                vis1.erase(vis1.find(b[i]));
        }
    int cnt1[10], cnt2[10];
    memset(cnt1, 0, sizeof(cnt1));
    memset(cnt2, 0, sizeof(cnt2));
    for (auto p : vis1) {
        int x = p.first, y = p.second;
        if (y == 0) continue;
        if (x < 10) cnt1[x] += y;
        else cnt1[f(x)] += y, tot += y;
    }
    for (int i = 1; i <= n; ++i)
        if (c[i] == 0) {
            if (b[i] < 10) ++cnt2[b[i]];
            else ++cnt2[f(b[i])], ++tot;
        }
    for (int i = 1; i < 10; ++i) {
        int d = min(cnt1[i], cnt2[i]);
        cnt1[i] -= d, cnt2[i] -= d;
    }
    for (int i = 2; i < 10; ++i)
        tot += cnt1[i] + cnt2[i];
    return tot;
}
int main() {
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

D题 Letter Picking(SG博弈)

给定一个长度为 \(n\) 的字符串 \(s\)(保证 \(n\) 是偶数)。

现在 Alice 和 Bob 进行游戏,Alice 先手,初始状态下每个人都有一个空的字符串。在每一个回合,玩家可以从字符串 \(s\) 的开头或者结尾拿走一个字符,放在自己的字符串的头部。游戏整体结束后(\(s\) 变空),字典序更小的那个人赢(在相同的情况下是平局)。

\(T\leq 10^3,|s|\leq 2*10^3\)

一个不是很典型的 SG 博弈题(一个是因为有平局,还有一个是因为两轮操作才被认为是一个阶段),我们记 0 为 Bob 赢,1 为平局,2 为 Alice 赢(只记录 Alice 先手的状况,因为 Bob 纯被动应付的)。

假设此时区间是 \([l,r]\),那么接下来有四种走向:

  1. 我拿头部,对方拿头部,变成子问题 \([l+2,r]\),得到 SG 值为 \(x_1\)
  2. 我拿头部,对方拿尾部,变成子问题 \([l+1,r-1]\),得到 SG 值为 \(x_2\)
  3. 我拿尾部,对方拿头部,变成子问题 \([l+1,r-1]\),得到 SG 值为 \(x_3\)
  4. 我拿尾部,对方拿尾部,变成子问题 \([l,r-2]\),得到 SG 值为 \(x_4\)

假设我们拿了 \(c_1\),对方拿了 \(c_2\),后续的新区间是 \([l',r']\),那么有:

  1. 当 \(c_1=c_2\) 时,当前状态的 SG 和 \([l',r']\) 的SG完全相同
  2. 当 \(c_1<c_2\) 时,当前状态的 SG 视情况而定:
    1. 如果 \([l',r']=0\),那么该状态就是 0
    2. 如果 \([l',r']>0\),那么该状态就是 2
  3. 当 \(c_1>c_2\) 时,当前状态的 SG 视情况而定:
    1. 如果 \([l',r']=2\),那么该状态就是 2
    2. 如果 \([l',r']<2\),那么该状态就是 0

边界条件:当 \(l=r+1\) 时,\(SG=0\)。

最终 SG,我们需要小处理一下:对方的行动往坏处想,我方的行动往好处用,所以是 \(\max(\min(x_1,x_2),\min(x_3,x_4))\)。

直接记忆化奔着搜一遍就行,复杂度 \(O(n^2)\)。

#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int n;
char s[N];
/*
0 : Bob win
1 : Draw
2 : Alice win
*/
//
int SG[N][N];
int dfs(int l, int r) {
    if (l == r + 1) return 1;
    if (SG[l][r] != -1) return SG[l][r];
    //
    int x1, x2, x3, x4;
    //choice 1
    if (s[l] == s[l + 1])
        x1 = dfs(l + 2, r);
    else if (s[l] < s[l + 1])
        x1 = dfs(l + 2, r) > 0 ? 2 : 0;
    else
        x1 = dfs(l + 2, r) < 2 ? 0 : 2;
    //choice 2
    if (s[l] == s[r])
        x2 = dfs(l + 1, r - 1);
    else if (s[l] < s[r])
        x2 = dfs(l + 1, r - 1) > 0 ? 2 : 0;
    else
        x2 = dfs(l + 1, r - 1) < 2 ? 0 : 2;
    //choice 3
    if (s[r] == s[r - 1])
        x3 = dfs(l, r - 2);
    else if (s[r] < s[r - 1])
        x3 = dfs(l, r - 2) > 0 ? 2 : 0;
    else
        x3 = dfs(l, r - 2) < 2 ? 0 : 2;
    //choice 4
    if (s[l] == s[r])
        x4 = dfs(l + 1, r - 1);
    else if (s[r] < s[l])
        x4 = dfs(l + 1, r - 1) > 0 ? 2 : 0;
    else
        x4 = dfs(l + 1, r - 1) < 2 ? 0 : 2;
    //make choice
    int x = max(min(x1, x2), min(x3, x4));
    return SG[l][r] = x;
}
int solve() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    for (int i = 0; i <= n + 1; ++i)
        for (int j = 0; j <= n + 1; ++j)
            SG[i][j] = -1;
    return dfs(1, n);
}
int main() {
    int T;
    cin >> T;
    while (T--) {
        string ans[3] = {"Bob", "Draw", "Alice"};
        cout << ans[solve()] << endl;
    }
    return 0;
}

(似乎也有人是直接贪心过的)

标签:10,int,题解,CF,dfs,else,leq,135,SG
From: https://www.cnblogs.com/cyhforlight/p/16725555.html

相关文章

  • Codeforces Round #822 Div.2 整场题解
    目前还有E,F没有更新。A.SelectThreeSticks直接对\(a\)排序,选出来的木棍一定是相邻三项,都往中间靠更优。B.Bright,Nice,Brilliant最优方案就是每一行第一个......
  • 牛客题解 装进肚子
    链接:https://ac.nowcoder.com/acm/problem/14721来源:牛客网题解作者岛田小雅这道题刚拿到手,就感觉是个很简单(事实证明并不是很简单)的贪心。虽然我不是很会判断贪心的......
  • pycharm打字卡顿问题解决
    问题描述:我在pycharm中使用的远程服务器中的环境,工程也是本机映射到远程环境中,在某次断网以后,再次使用就变得非常卡,具体现象就是我码字要等,整个pycharm就无法点击,过了5秒以......
  • CF627F Island Puzzle.
    容易观察到一次操作是将\(0\)移动到一个相邻的节点上,而且操作可逆转。所以对于不加边的情况方案是唯一的,直接模拟一下看看是不是相等的就好。对于加一条边来说,我们先将......
  • P1347 排序 题解
    题干交了8次,下载了3个测点.....首先这个题,很容易想到用拓扑如果有“X$<$Y”,就建立一条从X到Y的有向边要考虑到,如果排序成立,必须满足入度为0的点只有一个并且出度为0的......
  • P5089 [eJOI2018] 元素周期表 题解
    \(Preface\)主要是想刷点咕值然后就写了一写。。。顺便扔到博客园这边。题解题目传送门这道题嘛..主要还是找性质推规律。拿到题,第一眼:噢噢爆搜啊。第二眼:噢噢贪心啊......
  • P4484题解
    很神奇的状态。。。。。。很难想象这是一个人能在考场上想到的状态。对于一个排列\(p\),设\(f_i\)表示以\(p_i\)结尾的LIS的长度。考虑排列计数的套路令所有元素......
  • Vue中使用benz-amr-recorder插件实现播放amr音频文件以及在线url跨域问题解决
    场景需要做一个Android端和Web端的聊天室,Android端的录音音频文件为.amr格式,除了通过后台server端转码之后,是否可以通过插件在前端直接播放amr的音频文件。benz-amr-rec......
  • CF1089D Distance Sum
    传送门tourist出的绝世好题思路首先,考虑一个范围更广的问题:\[\sum_{u=1}^{n-1}\sum_{v=u+1}^nw_uw_vd(u,v)\]\(w_u\)表示\(u\)的点权,\(d(u,v)\)表示\(u,~v\)......
  • 【NEERC2011】【GYM100085F】Flights 题解
    【NEERC2011】【GYM100085F】Flights题意给定\(n\)个抛物线,保证开口向下且与\(x\)轴的两个交点都在\(x\)轴正半轴或原点。\(m\)次询问,每次询问给定四个数\(L,R,......