Practice.

A. Game with Board

题意

给定 \(n\)\(1\),定义操作为选定至少两个相同的数,将其从序列中移除,并插入他们的和。

对于 \(Alice\)\(Bob\) 之间的博弈,规定 \(Alice\) 先手,当某个玩家 无法 执行操作时,该玩家 获胜

输出最后获胜的玩家。

思路

首先,如果 \(n\) 比较大的时候,\(Alice\) 就可以选择 \(n - 2\)\(1\),这样 \(Bob\) 只能将剩余的两个 \(1\) 选上,最后序列剩余两个不同的数,从而 \(Alice\) 必胜。

上面的必胜策略需要保证 \(n - 2 > 2\),也就是 \(n > 4\) 的时候 \(Alice\) 有必胜策略。

我们不难发现,当 \(2 \leq n \leq 4\) 的时候,无论 \(Alice\) 怎么选,最后 \(Bob\) 一定会无法操作,因而 \(Bob\) 必胜。

因而,判断 \(n\) 的大小即可。

时间复杂度:\(O(1)\)

对应AC代码

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

#define int long long
#define pii pair<int, int>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int n;
    cin >> n;
    cout << (n > 4 ? "Alice" : "Bob") << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

不要看错题(

B. Keep it Beautiful

题意

给定一个序列,定义满足下面任意一个条件的序列是美丽的:

  1. 整个序列不递减;

  2. 从头开始选择一段连续的子序列,将其和剩余的子序列交换位置,得到的新序列不递减

现在,给定一个操作序列,定义操作如下:

给定一个数 \(x\),如果将其插入到当前序列的最后,得到的新序列是美丽的,那么执行插入操作,否则不插入。

对于每一次操作,输出是否可以操作成功,用二进制字符串表示。

思路

显然,我们分讨模拟即可。

我们维护当前序列的头 \(st\) 和尾 \(ed\) 对应的数值,如果我们没有交换过位置,那么我们只需满足 \(x \geq ed\)

那如果不满足,我们就需要交换位置,显然要满足条件的话,我们就只能把 \(x\) 作为新序列的头,把前面的全都移到后面去。因而,如果之前就执行过交换操作,这次插入操作就失败了。

那么,如果交换过位置,且满足 \(x \geq ed\),我们就需要添加一个条件 \(x \leq st\),保证交换后依然美丽。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int n;
    cin >> n;
    int st;
    cin >> st;
    int ed = st;
    n --;
    cout << 1;
    bool f = false;
    while(n --){
        int x;
        cin >> x;
        if(x >= ed){
            if(f && x > st) cout << 0;
            else{
                cout << 1;
                ed = x;
            }
        }else{
            if(!f && x <= st){
                cout << 1;
                f = true;
                ed = x;
            }else cout << 0;
        }
    }
    cout << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

模拟模拟

C. Ranom Numbers

题意

给定一个由 "\(A\)", "\(B\)", "\(C\)", "\(D\)", "\(E\)" 组成的字符串,其中 \(ABCDE\) 对应的值分别为 \(1, 10, 100, 1000, 10000\),因而五个字符具有大小关系。

给定字符串的值的运算方式:

遍历每一位的字符。如果该字符 小于等于 后面的所有字符,那么取正号,否则取负号。将每一位乘上符号后累加得到字符串的值。

现在,需要选择一个字符,将其修改为任意五个字符的其中一个,输出修改后的最大值。

思路

由后面的状态约束前面的状态,我们不难想到 \(dp\)

为了方便,我们不妨反转字符串,这样我们就可以从前往后递推了。

如上,我们可以定义一个三维 \(dp\)\(dp[i][mx][c]\) 表示第 \(i\) 位在前面所有字符的最大值是 \(mx\) 且前面是否有修改的状态下的最大值,\(c\) 表示前面是否修改过。

那么,我们只需枚举第 \(i\) 位置放什么字符,以及前面是否有出现修改,因而可以得出第 \(i\) 位置是否 可以 并 需要 修改,并更新前 \(i\) 个字符的最大值。

最后,\(dp[n]\) 的所有状态的最大值就是答案。

当然,因为只有相邻的两个 \(dp[i]\) 有递推关系,所以我们用两个 \(dp\) 数组也是可以的,从而优化空间复杂度。

时间复杂度:\(O(10n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

const vector<int> p = {1, 10, 100, 1000, 10000};

void solve() {
    string s;
    cin >> s;
    reverse(all(s));
    int n = s.size();
    s = "#" + s;
    vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(5, vector<int>(2, -inf)));
    dp[0][0][0] = 0;
    for(int i=1;i<=n;i++){
        int self = s[i] - 'A';
        for(int mx = 0; mx < 5; mx ++){ //枚举前面的最大值
            for(int c = 0; c <= 1; c ++){ //前面有没有改过
                for(int now = 0; now < 5; now ++){ //当前位置填什么
                    if(now != self && c == 1) continue;
                    int mx2 = max(mx, now),
                        ok = (now != self || c == 1) ? 1 : 0;
                    dp[i][mx2][ok] = max(dp[i][mx2][ok], dp[i - 1][mx][c] + (now >= mx ? 1 : -1) * p[now]);
                }
            }
        }
    }
    int ans = -inf; //玛德蠢了打了个0
    for(int mx = 0; mx < 5; mx ++){
        for(int c = 0; c <= 1; c ++){
            ans = max(ans, dp[n][mx][c]);
        }
    }
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

第一眼读假,草

D. Pairs of Segments

找个时间再做,待补充