个人感觉难度划分:A/CBDEFG/JHIK

题解按照题目顺序

B题

需要判断两个字符串是否存在,同时存在或者同时不存在时输出NO,遍历一遍字符串,找到'A'时检验是否是两个之一,最后判断即可。注意:有些人使用substr来判断,也是可以的,但是注意substr第二个参数是字符串长度,而不是终止位置。

#include <bits/stdc++.h>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    cin >> s;
    string a1 = "Association_for_Computing_Machinery";
    string a2 = "Air_Combat_Maneuvering";
    bool t1 = 0, t2 = 0;
    int n = s.size();
    auto check = [&](string s, string k, int i) -> bool
    {
        int len = k.size();
        for (int j = 0; j < len; j++){
            if (s[i + j] != k[j]){
                return 0;
            }
        }
        return 1;
    };
    for (int i = 0; i < n; i++){
        if (s[i] == 'A'){
            if (!t1){
                t1 = check(s, a1, i);
            }
            if (!t2){
                t2 = check(s, a2, i);
            }
        }
    }
    if (t1 && t2 || t1 == 0 && t2 == 0){
        cout << "NO\n";
    }else{
        cout << "YES\n";
    }
    return 0;
}

C题 需要判断是否超出int范围,注意题目中|x|<故可以使用long long 存下,判断是否超过INT_MAX即可.注意:x为负数时,最小可到-INT_MAX-1,不能取绝对值直接判

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const i64 MAX = INT_MAX;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    i64 t;
    cin >> t;
    if(t < 0 && abs(t) > MAX + 1 || t > MAX){
        cout << "YES\n";
    }else{
        cout << "NO\n";
    }
    return 0;
}

D题

由于"henu"并不存在相等的子字符串,所以不可能存在范围相交的"henu",故枚举即可

#include <bits/stdc++.h>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    string s;
    cin >> n >> s;
    int ans = 0;
    for (int i = 0; i < n - 3; i++){
        if (s[i] == 'h' && s[i + 1] == 'e' && s[i + 2] == 'n' && s[i + 3] == 'u'){
            ans++;
        }
    }
    cout << ans << "\n";
    return 0;
}

E题

开一个二维数组f[i][j]代表第i个盒子和第j个颜色,模拟即可

#include <bits/stdc++.h>
using namespace std;
int f[101][101];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, q;
    cin >> n >> q;
    while (q--){
        int op;
        cin >> op;
        if (op == 1){
            int x, y, col;
            cin >> x >> y >> col;
            if (f[x][col] == 0){
                continue;
            }else{
                f[x][col]--;
                f[y][col]++;
            }
        }else if (op == 2){
            int x, col;
            cin >> x >> col;
            f[x][col]++;
        }else{
            int x, col;
            cin >> x >> col;
            cout << f[x][col] << "\n";
        }
    }
    return 0;
}

F题

可以通过将时间全部转化为秒来计算,由于调时间时,到24:00:00会重新变成00:00:00,故不能直接计算判断。
有解,由于n>=t时,已经循环了,故枚举n [0,t)即可通过
对于输入问题,可以使用很多方式解决:scanf(),由于限制每一位都是两位,也可以substr
同时也可以使用char来解决':'问题,题解使用更C++的方式写输入

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int MOD = 24 * 60 * 60;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    i64 p = 0, s = 0, t = 0;
    auto read = [&]() -> i64
    {
        i64 a, b, c;
        char x, y;
        cin >> a >> x >> b >> y >> c;
        return a * 60 * 60 + b * 60 + c;
    };
    p = read(), s = read(), t = read();
    i64 k = s - p;
    for (i64 i = 0; i < t; i++){
        if (k > 0 && k % t == 0){
            cout << k / t << '\n';
            return 0;
        }
        k += MOD;
    }
    cout << -1 << '\n';
    return 0;
}

G题

本题由于数据量并不是很大,实际上关闭同步流后,按照题意模拟暴力也可以能过此题。
讲一下非暴力如何过,由于一个数异或两次相同的数,结果不变。
对于先修改最后查询的题,可以使用前缀和和差分优化。

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    i64 n, q1, q2;
    cin >> n >> q1 >> q2;
    vector<i64> f(n + 1), sum(n + 2);
    for (int i = 1; i <= n;i++){
        cin >> f[i];
    }
    while (q1--){
        i64 l, r, x;
        cin >> l >> r >> x;
        sum[l] ^= x;
        sum[r + 1] ^= x;
    }
    for (int i = 1; i <= n;i++){
        sum[i] ^= sum[i - 1];
        f[i] ^= (sum[i] ^ f[i - 1]);
    }
    while (q2--){
        i64 l, r;
        cin >> l >> r;
        cout << (f[r] ^ f[l - 1]) << '\n';
    }
    return 0;
}

H题

本题数据量较大,如果使用暴力复杂度为,只能过66.67%,需要考虑优化。
看本题希望能自己画图,辅助理解,语文不行,只能尽量描述了
注意到本题只有'1'才能产生贡献,可以将行和列分开看,其中列比较简单,无论如何移动,在本次不加入'1'时,列产生的答案数量不变
若加入的是'1'则会将后面的数往后移动,仅当上一行该位置的数也是'1'时,才会重复
故只需要记录m个位置是否出现过即可,使用map来记录,t时刻列产生map.size()个贡献
对于行,我没有想到非常巧妙的方式能算,于是我使用三个数组来模拟,f[i]表示当前行添加第i个数时会产生的贡献,pref[i]是当前行添加第i个数字时,上一行剩下的数字产生的贡献,如果pref[i]=1,尽管目前行没有添加'1',f[i]仍然为1,对于运算过后的行,每一行都会固定经过这些数字,可以将运算过的行数值放入sum中.
故每个时刻产生的贡献为map.size()+sum;

#include<bits/stdc++.h>
using namespace std;
void solve(){
    int n,m;
    string s;
    cin>>n>>m>>s;
    s=" "+s;
    unordered_map<int, bool> col;
    vector<int> f(m+1),pref(m+1),sum(m+1);
    for(int i=1;i<=n;i++){
        bool ism=false;//判断该行是否添加过'1'
        int k=0;//记录该行有几个'1',方便记录pref
        for(int j=1;j<=m;j++){
            f[j]=0;
            if(s[(i-1)*m+j] =='1'){
                k++;
                col[j]=1;
                ism=true;
            }
            if(ism || pref[j]){//若已经添加过1,或者上一行剩下的数字有1
                f[j]=1;
            }
            sum[j]+=f[j];
            cout<<sum[j]+col.size()<<" ";
            pref[j]=0;
        }
        for(int j=1;j<=m;j++){//模拟pref
            if(s[(i-1)*m+j]=='1'){
                k--;
            }
            if(k){
                pref[j]=1;
            }
        }
    }
    cout<<"\n";
    return;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

I题

需要划分成k个区间,使区间和非递减,本题使用dp[i][j]解决,其意义代表第i个元素结尾的第j个区间
注意:由于需要递增, 我们更希望讨论第x区间时,第x-1区间的总和越小越好,故需要初始化为inf。
讨论区间和时,使用前缀和优化求和

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve(){
    i64 n, k;
    cin>>n>>k;
    vector<i64> f(n + 1);
    for (int i = 1; i <= n;i++){
        cin >> f[i];
        f[i] += f[i - 1];
    }
    vector<vector<i64>> dp(n + 1, vector<i64>(k + 1,LLONG_MAX));
    dp[0][0] = -LLONG_MAX;
    for (int i = 1; i <= n;i++){//第i个元素
        for (int j = 1;j<=i;j++){//区间的第一个元素
            for (int l = 1; l <= k;l++){//第k个区间
                if(f[i]-f[j-1]>=dp[j-1][l-1]){
                    dp[i][l] = min(dp[i][l], f[i] - f[j - 1]);
                }
            }
        }
    }
    if(dp[n][k]!=LLONG_MAX){
        cout << "Yes\n";
    }else{
        cout << "No\n";
    }
    return;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
    return 0;
}

J题

此题没有看上去这么难,由于(1,1)和(n,m)不能重复来回移动,所以剩下个格子,类似于华容道,剩下的格子中最少需要两个空格子(通过移动可以到右下角)就能稳定到右下角,故可以看作有一个的背包存卡牌,由于需要(n,m)从上往下排列好,所以需要以递减的方式移动到(n,m),我是使用大根堆来模拟的,也可以标好号排序来模拟。
注意:本题若已经超过了n*m-4个,不能直接终止,因为数据并没有输入完,会导致后面数据错位,所以要么提前输入完模拟,要么等运行完再输出

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
void solve(){
    i64 n, m, k;
    cin >> n >> m >> k;
    i64 stg = n * m - 4;
    priority_queue<i64> que;
    i64 pt = k;
    bool ans = true;
    for (int i = 0; i < k; i++){
        while (!que.empty() && que.top() == pt){
            pt--;
            que.pop();
        }
        i64 x;
        cin >> x;
        if (x == pt){
            pt--;
        }else{
            que.push(x);
        }
        if (que.size() > stg){
            ans = false;
        }
    }
    if(ans){
        cout << "YES\n";
    }else{
        cout << "NO\n";
    }
    return;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--){
        solve();
    }
    return 0;
}

K题

首先题目给出了我能杀等级x的史莱姆,所以数值其实并不重要,对于我来讲只有能自己解决的i个史莱姆和只能靠天雷解决的j个史莱姆
一开始,我思考是否有概率的通项公式解,但是很难找到,于是想到使用动态规划dp[i][j]表示还剩i我能解决,j个只能靠天雷解决的状态
但是动态规划比较难写,于是使用记忆化搜索来解决,对于某个状态,dfs(i,j)剩下i个我自己可以杀的史莱姆,j个只能靠天雷杀的史莱姆,对于我来讲,我只能将其变成dfs(i-1,j),然后天雷可以造成两种状态dfs(i-1,j-1)或者dfs(i-2,j);其中前一种的概率是p1=后一种概率是p2=,故转移为 对于概率使用逆元即可,题目也给出了方法。
数据较大,需要开long long,并且需要注意爆long long
tips:可以去做周赛80的G题,方法与本题类似,可以巩固一下

using namespace std;
using i64 = long long;
using u64 = unsigned long long;
const i64 MOD = 998244353;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    i64 n, x;
    cin >> n >> x;
    i64 l = 0, r = 0;
    for (int i = 0; i < n; i++){
        i64 k;
        cin >> k;
        if (k <= x){
            l++;
        }
        else{
            r++;
        }
    }
    vector<vector<i64>> f(l + 1, vector<i64>(r + 1));
    vector<vector<bool>> vis(l + 1, vector<bool>(r + 1));
    i64 t = n / 2 + n % 2;
    if (l < t){
        cout << 0 << "\n";
        return 0;
    }
    auto ksm = [&](i64 a, i64 b) -> i64{
        i64 res = 1;
        a %= MOD;
        while (b){
            if (b & 1){
                res = res * a % MOD;
            }
            a = a * a % MOD;
            b >>= 1;
        }
        return res;
    };
    function<i64(i64, i64)> dfs = [&](i64 i, i64 j) -> i64
    {
        if (i < 0){
            return 0;
        }
        if (j == 0){
            return 1;
        }
        if (vis[i][j]){
            return f[i][j];
        }
        if (i == 0){
            return 0;
        }
        i64 pt = ksm(i - 1 + j, MOD - 2);
        f[i][j] = (dfs(i - 2, j) * (i - 1) % MOD * pt % MOD + dfs(i - 1, j - 1) * j % MOD * pt % MOD) % MOD;
        vis[i][j] = 1;
        return f[i][j] % MOD;
    };
    cout << dfs(l, r) % MOD << "\n";
    return 0;
}