(题解)Codeforces Round #884 (Div. 1 + Div. 2) A B C D

A. Subtraction Game

原题指路:Problem - A - Codeforces

题意(1s):

有\(t(1 \le t \le 100)\)组测试数据,每组数据给出两个数\(a\)和\(b\),表示两个人一次能拿的石子个数,现在询问石子个数\(n\)为多少时,后手存在必胜策略(当一方不能拿石子时,另一方胜利。) 

思路:

这就是一个\(a+b\)问题的plus版本。当\(n = a + b\)的时候,无论先手怎么拿,后手都能拿拿完剩下的让先手不能再拿。

代码:

#include<bits/stdc++.h>
using namespace std;

void solve() {
    int a, b;
    cin >> a >> b;
    cout << a + b << endl;
}

int main() {
    CaseT
    solve();
    return 0;
}


B. Permutations & Primes

原题指路:Problem - B - Codeforces

题意(1s):

有\(t(1 \le t \le 10^4)\)组测试数据,每组数据给出一个数\(n\),表示有\(1\) ~ \(n\)个数,现在问,将这\(n\)个数如何排列,能够使序列中有最多的子序列,满足其序列的MEX为质数。

MEX是指,在这一个序列中,最小的不存在的正整数。比如MEX(1, 2, 3) = 4;MEX(1, 2, 4) = 3。

思路:

①,如果子序列中没有1,那么这个子序列的MEX就为1,这个子序列不满足条件。如果这个子序列中有1无2,那么这个子序列的MEX=2,如果这个子序列有1, 2,无3,那么这个子序列的MEX=3

②,因此如果我们将1放在序列中间,2和3分别放在序列的两端,那么我们就能够得到更多的满足题目条件的子序列。

③,在这种情况下,子序列的个数是

$$sum = \begin{cases}(\frac{n + 1}{2})^2 - 1 \qquad \qquad, n为奇数\\\frac{n}{2} \times (\frac{n + 1}{2} + 1) - 1 \quad, n为偶数\end{cases}$$

推导就是组合数学,1在序列中间,然后求所有包含1的子序列个数(包括只有1本身),然后最后减去将整个数列(我们计算的是,MEX值一定为质数的子序列个数)。如果\(n+1\)也为质数的话,就不用减1了。

最后就是输出这个数组即可,打CF的时候我想多了,所以是摇摆着将数字一左一右的放在两边,但是实际上是不需要的,中间是1,两边分别是2和3即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)
const int N = 2e5 + 10;

int a[N];

void solve() {
    int n; cin >> n;
    int i = 1; int j = n;
    int mid = i + j >> 1;
    a[mid] = 1;
    int idx = 2;
    while (idx <= n) {
        if (i != mid) {
            a[i] = idx;
            idx++;
            i++;
        }
        if (idx > n) break;
        a[j] = idx;
        idx++;
        j--;
    }
    for (int i = 1; i <= n; i++) {
        cout << a[i] << " \n"[i == n];
    }
}

int main() {
    CaseT
    solve();
    return 0;
}


C. Particles

原题指路:Problem - C - Codeforces

题意(1s):

有\(t(1 \le t \le 10^4)\)组测试数据,每组测试数据先给出链表长度\(n\),接下来是\(n\)个数。然后你可以进行一个操作,就是删除掉链表中的某个数,如果删除的是链表中间的某个数,那么删掉的这个数左右两边的数就会合并成一个新的数,如果是删除链表两端的数,那么就没别的反应。最后将整个链表删除到只剩一个数,求剩的这个数最大是多少。

思路:

①:如果链表左右两端是负数,那么这些数就是白给的,直接删掉就好。

②:两个相邻的数,一定加不到一起。

③:现在假设我们得到的是一串全部为正数的串,那么显然我们能得到的最优解一定是所有间隔的元素之和。也就是最大的要么是 第1,3,5,7,9......个(奇数)元素相加,要么是第2,4,6,8......个(偶数)元素相加。

④:我们删掉了中间的一个数之后,两边的两个数合并,不会影响前后其他元素的奇偶性。比如有序列\(x_1, x_2, x_3, x_4, x_5, x_6, x_7\),我们将\(x_4\)删除,\(x_3\)与\(x_5\)合并为\(x_{3+5}\)得到序列\(x_1, x_2, x_{3+5}, x_6, x_7\),我们可以发现,原序列中\(x_1, x_2, x_6, x_7\)这几个数的所在的位置的奇偶性都没有发生改变。

⑤:由③④可见最后答案要么是,序列中的奇数项相加,要么是序列中的偶数项相加。当然相加的时候,我们要把负数项删掉,只留下正数项。可以放心删除,因为已经说了不改变后续项的奇偶性,直接删除就好了。

⑥:最后还需要特判一种情况,就是序列中全部都是非正数项,那么我们进行步骤五的时候,算出来两种情况的答案都是0,一个都没算。我们在代码中对这种情况要进行一个特判。在这种情况下,我们的操作应该是将链表中的数,只留下一个最大的(绝对值最小的)数,其他的可以全部一个个删除掉。我们只需要在遍历一遍找到最大值即可。

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;

ll a[N];

void solve() {
    int n; cin >> n;
    ll ans1 = 0;
    ll ans2 = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if ((i & 1)) {
            if (a[i] > 0) {
                ans1 += a[i];
            }
        }
        else {
            if (a[i] > 0) {
                ans2 += a[i];
            }
        }
    }

    if (!ans1 && !ans2) {
        ll ans = -INF;
        for (int i = 1; i <= n; i++) {
            ans = max(ans, a[i]);
        }
        cout << ans << endl;
        return;
    }

    cout << max(ans1, ans2) << endl;

}

int main() {
    CaseT
    solve();
    return 0;
}

吐槽:干啊,一开始完全没想到删除元素不改变后面项的奇偶性,于是就开始各种预处理,写了一坨一坨的dp方程式,主要代码一百多行。然后还WA了,气死了!


D. Row Major

原题指路:Problem - D - Codeforces

题意(2s):

有\(t(1 \le t \le 10^4)\)组测试数据,每组测试数据给出一个数\(n\),表示字符串的长度,接下来要你输出符合下述条件的字符串(数据保证一定有解)。

该字符串中的字符全部由小写的英文字母组成。且英文字母的种类数要最少。

按照下面这种情况分布,相邻的字母不相同。

思路:

找到第一个不能够整除\(n\)的数\(i\),然后前\(i\)个英文字母互不相同,最后不停地复制这串英文字母即可。比如\(n = 24\)时,\(1\mid24,2\mid24,3\mid24, 4\mid24,5\nmid24\),我们只需要输出abcdeabcdeabcde......即可。因为我们只有在相邻5的倍数时,才会有相等的字母,而这个字符串的长度又不能被5整除(自然也不能被5的倍数整除),因此在这种情况下,其字符串在能在满足情况的条件下所含字母种类最少。

代码:

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)

void solve() {
    int n; cin >> n;
    if (n == 1) {
        cout << "a" << endl;
        return;
    }
    if (n == 2) {
        cout << "ab" << endl;
        return;
    }

    int sum = 0;
    for (int i = 2; i <= n; i++) {
        if (n % i != 0) {
            sum = i;
            break;
        }
    }

    char c = 'a';
    for (int i = 1; i <= n; i++) {
        cout << c;
        c++;
        if (i % sum == 0) c -= sum;
    }
    cout << endl;

}

int main() {
    CaseT
    solve();
    return 0;
}

 


F1. Min Cost Permutation (Easy Version)

原题指路:Problem - F1 - Codeforces

等白天题目出来后再补题,赛时WA2了,可恶。等过题了再回来写。(如果补不出来就补不出来了)。


这一场前几题基本都差不多是构造题了,个人感觉都挺诈骗的。看着挺复杂的,但是如果思路没被骗进沟里的话,代码和实现还是很简单的。主要是在思维上面,对码力的要求比较少。



$$\color{blue}{绝}\color{yellow}{域}\color{red}{殊}\color{purple}{方}\color{green}{画}\color{gold}{秋}\color{cyan}{雁}\color{lavender}{,}\color{magenta}{笑}\color{orangered}{看}\color{springgreen}{春}\color{gray}{风}\color{aquamarine}{阅}\color{violet}{千}\color{greenyellow}{帆}\color{thistle}{。}$$

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值