Contestant~. Rank 881. Rating +7.

A. Prime Deletion

题意

给定 \(\{0, 1, \ldots, 9\}\) 的一种排列,定义一次操作为在排列剩余元素个数 大于2 的条件下,任意删去一个数字。

任意次操作后,输出排列拼接而成的数字,并满足该数字是质数。

思路

因为 \(13, 31\) 都是质数,并且 \(1\) 一定在 \(3\) 的左边或者右边,因而判一下 \(1\) 的位置即可。

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

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    string s;
    cin >> s;
    int i1 = -1, i3 = -1;
    for(int i=0;i<9;i++){
        if(s[i] == '1') i1 = i;
        if(s[i] == '3') i3 = i;
    }
    if(i1 < i3) cout << "13\n";
    else cout << "31\n";
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

太奇妙了

B. Two Binary Strings

题意

给定两个长度均为 \(n\) 的二进制字符串 \(a, b\),满足两个字符串都以 \(0\) 开头,\(1\) 结尾。

定义一次操作为选定任意一个字符串 \(s\),并选定 \(1 \leq l < r \leq n\),满足 \(s_l = s_r\),并将 \(s_{[l, r]}\) 内所有字符赋值为 \(s_l\)

任意次操作后,输出两个字符串是否可能相同。

思路

既然都以 \(0\) 开头,\(1\) 结尾,那么如果存在某个位置 \(i\),满足 \(a_i = b_i = 0\),那么我们就可以将 \(a_{[1, i]}, b_{[1, i]}\) 内所有字符改为 \(0\)

同理,如果存在某个位置 \(j\),满足 \(a_j = b_j = 1\),那么我们就可以将 \(a_{[j, n]}, b_{[j, n]}\) 内所有字符改为 \(1\)

显然,若 \(j = i + 1\),那么最后的字符串一定相等。

那么问题等价于:判断是否存在 \(i \in [1, n - 1]\),使 \(a_i = b_i = 0, a_{i + 1} = b_{i + 1} = 1\)

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

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    string s1, s2;
    cin >> s1 >> s2;
    int n = s1.size();
    s1 = " " + s1, s2 = " " + s2;
    for(int i=1;i<=n-1;i++){
        if(s1[i] == s2[i] && s1[i + 1] == s2[i + 1]){
            if(s1[i] == '0' && s1[i + 1] == '1'){
                cout << "YES\n";
                return;
            }
        }
    }
    cout << "NO\n";
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

如果没那个条件就成大模拟了(

C. Queries for the Array

题意

定义 已排序 为该序列不降序,若序列元素个数 \(< 2\),也判定为已排序。

对于一个未知的序列,一开始序列为空,给定操作序列对应的字符串,判断是否合法,其中操作如下:

  1. "0":当前序列未排序;

  2. "1":当前序列已排序;

  3. "+":加入任意一个数字;

  4. "-":删去 最后 一个数字

思路

首先,既然元素个数 \(<2\) 一定已排序,那么我们只需记录当前序列的长度 \(cnt\),如果出现 \(cnt < 2\),且当前字符为 \(0\),那么直接判 \(NO\)

其次,我们记最小的 未排序 序列的长度为 \(mn\),最长的 已排序 序列的长度 \(mx\)

那么,另一个不合法的条件是:\(mn \leq mx, mn > 0\)

我们可以考虑下面的构造:

  1. 遇到 \(+\),将 \(1\) 加入序列;

  2. 遇到 \(0\),将最后一个数改为 \(0\)

可以证明上面的构造是最优的,且恰好满足上述的合法判定条件。

那么我们考虑下面的维护:

  1. 遇到 \(+\),增加序列长度;

  2. 遇到 \(-\),减小序列长度。此时,如果 \(mx\) 和减小前的长度相等,那么我们就得去掉一个数,也就是减掉 \(1\)。同样的,如果 \(mn\) 和减小前的长度相等,那么可能会出现删除该数后整个序列有序,因而将 \(mn\) 修改为 \(0\)

  3. 遇到 \(1\)\(mx = cnt\)

  4. 遇到 \(0\),如果 \(cnt < 2\),那么直接判 \(NO\)。此时一并更新 \(mn\)

  5. 在处理完每个字符之后,判断 \(mn \leq mx, mn > 0\) 是否满足,满足直接判 \(NO\)

当然,也可以直接用线段树维护,不过去掉板子之后码量差不多。

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

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    string s;
    cin >> s;
    int mx = 0, mn = 0; //最大已排序前缀,最小未排序后缀
    int cnt = 0;
    for(auto &e : s){
        if(e == '+') cnt ++;
        else if(e == '-'){
            cnt --;
            if(mx == cnt + 1) mx = cnt;
            if(cnt < mn) mn = 0;
        }else if(e == '1'){
            mx = cnt;
        }else{
            if(cnt <= 1){
                cout << "NO\n";
                return;
            }
            if(mn == 0 || mn > cnt) mn = cnt;
        }
        if(mn <= mx && mn > 0){
            cout << "NO\n";
            return;
        }
    }
    cout << "YES\n";
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

线段树用在 \(C\) 还是太抽象了

D. Sorting By Multiplication

题意

给定一个序列 \(a\),定义一次操作为选定 一个区间 以及 任意一个数字 \(x\),并将区间内的所有数都乘上 \(x\)

\(x\) 可以为负的条件下,输出最小操作总数,使整个序列升序。

思路

显然,上一个数的状态可以转移到下一个数,且对应的变化是是否多一次操作,因而我们考虑动态规划。

具体来说,我们令 \(dp[i][j]\) 为前 \(i\) 个数让其升序的最小操作数,\(j\) 代表当前位置是否为负数。

那么,我们按照 \(a_{i - 1}, a_i\) 的大小进行分类讨论:

  1. \(a_{i - 1} < a_i\),此时如果该位为正数,我们就无需操作;否则我们得单独对该数进行一次操作,来让这个数比前一个 负数 大,显然这个时候,前面的数不可以是正数。

  2. \(a_{i - 1} > a_i\),此时如果该位是正数,我们就得单独对该数进行一次操作,来让这个数比前一个数大;否则,我们就无需操作,但需要满足上一个数是负数。

  3. \(a_{i - 1} = a_i\),此时无论如何,当前位置都得单独进行一次操作,来让这个数比前一个数大。

当然,初始化的时候 \(dp[1][1] = 1\)

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

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    vector<vector<int>> dp(n + 1, vector<int>(2)); //当前位置是不是负数
    dp[1][1] = 1;
    for (int i = 2; i <= n; i++) {
        dp[i][0] = dp[i - 1][1];
        dp[i][1] = dp[i - 1][1];
        if (a[i - 1] < a[i]) {
            dp[i][1] ++;
            dp[i][0] = min(dp[i][0], dp[i - 1][0]);
        } else if (a[i - 1] > a[i]) {
            dp[i][0] = min(dp[i][0], dp[i - 1][0] + 1);
        } else {
            dp[i][1] ++;
            dp[i][0] = min(dp[i][0], dp[i - 1][0] + 1);
        }
    }
    cout << min(dp[n][0], dp[n][1]) << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

主要难在设计状态(