锐评

  • 本套题目风格和天梯赛相近,难度略高于天梯赛。

  • IOI赛制是可以骗分的,对多少测试点就给多少分,比如题直接输出题直接输出都是有分的,F题、H题直接给输入原样输出也是有分的,比赛的时候不会别空着,IOI赛制没有罚时,错了也不扣分,可以大胆尝试。

  • 如果天梯赛之前字符串常用的函数()还不会就都别去打了。

  • 打天梯赛的没事好好做做往年的题,做多了就会发现天梯出题是有规律的:https://pintia.cn/problem-sets/994805046380707840/exam/problems/type/7

L1-1

直接输出即可,注意因为图片中的“相”字打错成了“柤“,题目也要输出相同的内容。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cout << "2026年,ICPC,我们再柤会!" << endl;
    return 0;
}

L1-2

首先当 是奇数时,无法恰好覆盖,证明如下:

  • 的砖块面积为,所以覆盖的总面积也一定是 的整数倍,也就是说覆盖的面积只能是偶数不能是奇数。

是偶数,我们把砖块竖着放置,每行 块,放 行,如下方左图。

是偶数,我们把砖块横着放置,每列 块,放列,如下方右图。

alt

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int a, b;
    cin >> a >> b;
    if(a % 2 && b % 2) cout << "No" << endl;
    else cout << "Yes" << endl;
    return 0;
}

L1-3

按题意模拟即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int W, T, ed, M, U, ud;
    cin >> W >> T >> ed >> M >> U >> ud;
    if(M == 1){
        cout << "Repair" << endl;
        cout << "No" << endl;
    }
    else{
        if(T == W){
            cout << "Run" << endl;
            cout << "No" << endl;
        }
        else{
            if(ed == ud){
                cout << "Stop" << endl;
                if(T + U > W) cout << "No" << endl;
                else cout << "Yes" << endl;
            }
            else{
                cout << "Run" << endl;
                cout << "No" << endl;
            }
        }
    }
    return 0;
}

L1-4

首先要分别得到的 二进制第位及以下的部分的 二进制第位以上的部分

  • 对于,可以用一个二进制第位及以下全是、第位以上全是的数,也就是来和进行按位与得到。

  • 对于,直接用即可得到。

接着,按题意模拟,判断,也就是的大小:

  • 如果,直接舍弃,答案就是

  • 如果,将舍去,给的二进制第位加,答案就是

另外,通过二进制推导可以得到,答案其实就是:

#include <iostream>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, k;
    cin >> n >> k;
    int mask = (1 << k) - 1;
    int low = n & mask;
    int high = n - low;
    if(low >= (1 << (k - 1))) cout << high + (1 << k) << endl;
    else cout << high << endl;
    return 0;
}
#include <iostream>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, k;
    cin >> n >> k;
    int res = ((n + (1LL << (k - 1))) >> k) << k;
    cout << res << endl;
    return 0;
}

L1-5

这题的题目其实叫 “//是什么意思”,但牛客的系统似乎直接也把这句话当注释忽略了

循环不断读入字符串,按顺序从左到右、从上到下遍历每个字符,分别用两个变量标记一下当前是不是在多行注释中、是不是在字符串中。

  • 如果遇到/*,且当前不在字符串和注释中,进入多行注释,遍历所有的字符都直接输出,此时如果遍历到*/,离开多行注释。
  • 如果遇到",且不在注释中,进入字符串,遍历所有的字符都直接原样输出,此书如果遍历",离开字符串。

模拟即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    string s;
    bool in_string = false;
    bool in_block = false;
    while(cin >> s){
        for(int i = 0; i < s.size(); i++){
            if(in_block) {
                if(i + 1 < s.size() && s[i] == '*' && s[i + 1] == '/'){
                    cout << "##";
                    i++;
                    in_block = false;
                }
                else cout << '#';
                continue;
            }
            if(!in_string){
                if(i + 1 < s.size() && s[i] == '/' && s[i + 1] == '/'){
                    cout << "##";
                    i += 2;
                    while(i < s.size()){
                        cout << '#';
                        i++;
                    }
                    break;
                }
                if(i + 1 < s.size() && s[i] == '/' && s[i + 1] == '*'){
                    cout << "##";
                    i++;
                    in_block = true;
                    continue;
                }
            }
            if(s[i] == '"') in_string = !in_string;
            cout << s[i];
        }
        cout << endl;
    }
    return 0;
}

L1-6

因为每年天梯赛字符串题的解答率都不尽如人意,因此出题组从几年前开始决定:每年的天梯赛的 15 分一定会有一道字符串题,另外一道则一定不是字符串题。

天梯赛每年必考的字符串操作,字符串中的常用函数一定要会用且记住。

  • 对于操作1:循环遍历每个字符,在里面再套一次循环遍历连续的相同的字符,统计数量。接着,在循环里不建议直接修改,会影响的大小,写不好极易出现边界错误,可以新开一个空串记录操作后的,遍历的时同时按替换规则一段一段往后接即可。
  • 对于操作2:首先用找到中第一次出现的位置,用找到中最后一次出现的位置。
    • 如果找不到 / 前面 / 有重叠部分:不变。
    • 否则直接将替换成之间的子串,或者将两端多余的地方删掉即可。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n;
    string s;
    cin >> n >> s;
    for(int i = 0; i < n; i++){
        char op;
        cin >> op;
        if(op == 'C'){
            string t;
            int p = 0;
            while(p < s.size()){
                char ch = s[p];
                int cnt = 0;
                while(p < s.size() && s[p] == ch){
                    cnt++;
                    p++;
                }
                t += to_string(cnt);
                t += ch;
            }
            s = t;
        }
        else{
            string s1, s2;
            cin >> s1 >> s2;
            int p1 = s.find(s1);
            int p2 = s.rfind(s2);
            if(p1 != -1 && p2 != -1 && p1 + s1.size() <= p2) s = s.substr(p1, p2 + s2.size() - p1);
        }
    }
    cout << s << endl;
    return 0;
}

L1-7

模拟时钟的变化,分别用变量来记录起始的小时、起始的分钟、目标的小时、目标的分钟,从起始时间开始,按时间规则模拟时间的变化,每次记录变化前的值和变化后的值,将两个值进行比对,判断有几位不同,累加到答案即可。

输入时间可以用格式化输出,判断不同位数时也可以用拼接成字符串后再判断。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    // ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int h, m, eh, em;
    scanf("%d:%d %d:%d", &h, &m, &eh, &em);
    int res = 0;
    while(h != eh || m != em){
        int a1 = h / 10;
        int a2 = h % 10;
        int a3 = m / 10;
        int a4 = m % 10;
        m++;
        if(m == 60){
            m = 0;
            h++;
            if(h == 24) h = 0;
        }
        int b1 = h / 10;
        int b2 = h % 10;
        int b3 = m / 10;
        int b4 = m % 10;
        if(a1 != b1) res++;
        if(a2 != b2) res++;
        if(a3 != b3) res++;
        if(a4 != b4) res++;
    }
    cout << res << endl;
}
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    // ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int h, m, eh, em;
    scanf("%d:%d %d:%d", &h, &m, &eh, &em);
    int res = 0;
    while(h != eh || m != em){
        char s1[6], s2[6];
        sprintf(s1, "%02d%02d", h, m);
        m++;
        if(m == 60){
            m = 0;
            h++;
            if(h == 24) h = 0;
        }
        sprintf(s2, "%02d%02d", h, m);
        for(int i = 0; i < 4; i++){
            if(s1[i] != s2[i]) res++;
        }
    }
    cout << res << endl;
    return 0;
}

L1-8

平移次数 很大,如果直接移动会超时。可以利用取模运算优化,因为平移次行/此列相当于没有移动。

然后模拟循环移位的操作即可,用一个临时数组存平移后的结果,再复制回原矩阵即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;
    char a[25][25];
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            cin >> a[i][j];
        }
    }
    int q;
    cin >> q;
    while(q--){
        int op, l, r, k;
        cin >> op >> l >> r >> k;
        l--, r--;
        if(op == 1){
            int t = k % m;
            for(int i = l; i <= r; i++){
                char tmp[25];
                for(int j = 0; j < m; j++){
                    tmp[(j + t) % m] = a[i][j];
                }
                for(int j = 0; j < m; j++){
                    a[i][j] = tmp[j];
                }
            }
        }
        else{
            int t = k % n;
            for(int j = l; j <= r; j++){
                char tmp[25];
                for(int i = 0; i < n; i++){
                    tmp[(i + t) % n] = a[i][j];
                }
                for(int i = 0; i < n; i++){
                    a[i][j] = tmp[i];
                }
            }
        }
    }
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            cout << a[i][j];
        }
        cout << endl;
    }
    return 0;
}

L2 -1

用队列来模拟当前在排队的序列,将序列前两个选手的实力值取出,其余选手的实力值入队。

循环模拟次比赛,每次比赛比较两位选手的实力值,实力值高的选手连胜场次,实力值低的选手入队,如果实力值高的选手连胜场次达到,在实力值低的选手入队后也入队。场上人数少于两人就从队首出队补位,新上场的选手连胜场次清零。模拟即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

struct Player{
    int val, cnt;
};

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, k;
    cin >> n >> k;
    queue<Player> q;
    for(int i = 0; i < n; i++){
        int s;
        cin >> s;
        q.push({s, 0});
    }
    Player p1 = q.front();
    q.pop();
    Player p2 = q.front();
    q.pop();
    for(int i = 1; i <= k; i++){
        if(i == k){
            int a = p1.val, b = p2.val;
            if(a > b) swap(a, b);
            cout << a << ' ' << b << endl;
        }
        if(p1.val > p2.val){
            p2.cnt = 0;
            q.push(p2);
            p1.cnt++;
            if(p1.cnt == 2){
                p1.cnt = 0;
                q.push(p1);
                p1 = q.front(); 
                q.pop();
                p2 = q.front(); 
                q.pop();
            } 
            else{
                p2 = q.front(); 
                q.pop();
            }

        } 
        else{
            p1.cnt = 0;
            q.push(p1);
            p2.cnt++;
            if(p2.cnt == 2){
                p2.cnt = 0;
                q.push(p2);
                p1 = q.front(); 
                q.pop();
                p2 = q.front(); 
                q.pop();
            } 
            else{
                p1 = q.front(); 
                q.pop();
            }
        }
    }
    return 0;
}
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, k;
    cin >> n >> k;
    queue<int> q;
    for(int i = 0; i < n; i++){
        int x;
        cin >> x;
        q.push(x);
    }
    int a = q.front(); 
    q.pop();
    int b = q.front(); 
    q.pop();
    int cnt = 0;
    for(int i = 1; i <= k; i++){
        if(i == k){
            if(a > b) swap(a, b);
            cout << a << ' ' << b << endl;
        }
        int winner, loser;
        if(a > b){
            winner = a;
            loser = b;
            cnt++;
        }
        else{
            winner = b;
            loser = a;
            cnt = 1;
        }

        q.push(loser);
        if(cnt == 2){
            q.push(winner);
            cnt = 0;
            a = q.front(); 
            q.pop();
            b = q.front(); 
            q.pop();
        }
        else{
            a = winner;
            b = q.front(); 
            q.pop();
        }
    }
    return 0;
}

L2-2

用三个变量记录答案,遍历一遍数组根据当前位置的数的奇偶,判断更新对应的变量即可。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
    int i = -1, j = -1, k = -1;
    for(int p = 1; p <= n; p++){
        int x;
        cin >> x;
        if(i == -1){
            if(x % 2) i = p;
        } 
        if(i != -1 && j == -1){
            if(x % 2 == 0){
                j = p;
                continue;
            }
        } 
        if(j != -1 && k == -1){
            if(x % 2 == 0) k = p;
        } 
        if(k != -1){
            if(x % 2){
                cout << "Yes" << endl;
                cout << i << ' ' << j << ' ' << k << ' ' << p << endl;
                return 0;
            }
        }
            
    }
    cout << "No" << endl;
}
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
    int i = -1, j = -1, k = -1;
    for(int p = 1; p <= n; p++){
        int x;
        cin >> x;
        if(i == -1){
            if(x % 2) i = p;
        } 
        else if(j == -1){
            if(x % 2 == 0) j = p;
        } 
        else if(k == -1){
            if(x % 2 == 0) k = p;
        } 
        else{
            if(x % 2){
                cout << "Yes" << endl;
                cout << i << ' ' << j << ' ' << k << ' ' << p << endl;
                return 0;
            }
        }
    }
    cout << "No" << endl;
}

L2-3

解法1:手机重量越大,要求越严格,满足单调性,可以二分答案。

设计函数,如果手机重量为,问题可以转化为:是否能找到对木棍,使得每一对乘积都

可以对数组排序后通过双指针来解决:

使用两个指针

  • 如果,说明可以和配对,合法对数加,将指针移到下一位置。
  • 如果,说明太小,即使配最大也不够,所以继续看下一个

最后比较合法配对数和的大小即可。

如果合法配对数大于等于,当前可以达到,继续向右半边尽可能找更大的答案。

如果合法配对数小于,当前大了,达不到,继续向左半边找可能满足的答案。

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
using LL = long long;

int a[100005];
int n, m;

bool check(LL x){
    int cnt = 0;
    int i = 0, j = n - 1;
    while(i < j){
        if((LL)a[i] * a[j] >= x){
            cnt++;
            i++; 
            j--;
        } 
        else i++;
    }
    return cnt >= m;
}

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for(int i = 0; i < n; i++){
        cin >> a[i];
    } 
    sort(a, a + n);
    LL l = 1, r = LLONG_MAX;
    while(l < r){
        LL mid = l + r + 1 >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l << endl;
    return 0;
}

解法2:贪心。将 按 从大到小的顺序排序后,最优方案即为:第 个支架用长度为 的木棍制作,第 个支架用长度为 的木棍制作,以此类推。

因此枚举 ,答案为

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
using LL = long long;

int a[100005];

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, m;
    cin >> n >> m;
    for(int i = 0;i < n; i++){
       cin >> a[i];
    } 
    sort(a, a + n, greater<>());
    LL res = LLONG_MAX;
    for(int i = 0;i < m;i++){
        LL t = (LL)a[i] * a[2 * m - 1 - i];
        res = min(res, t);
    }
    cout << res << endl;
    return 0;
}

L2-4

贪心配对,

首先用递归遍历统计每个节点的子树大小(节点数量)。

然后用进行贪心配对,遍历每个节点的所有子树,找到节点数量最大的子树并记录其根节点,比较该子树当前未用于配对的节点数量和其他所有的子树的节点数量和:

  • 如果小于,那显然所有子树未用于配对的节点都可以两两配对,直接累加答案返回即可。
  • 如果大于等于,将其他所有子树的所有节点都和该最大子树未用于配对的节点配对,累加答案,由于配对后最大子树内部还有部分节点未配对,接着递归处理该最大子树。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
using LL = long long;
const int N = 200005;

vector<int> g[N];
int sz[N];
LL res;

void dfs_sz(int u){
    sz[u] = 1;
    for(auto v : g[u]){
        dfs_sz(v);
        sz[u] += sz[v];
    }
}

void dfs(int u, int k){
    int sum = 0;
    int mx = -1;
    for(auto v : g[u]){
        sum += sz[v];
        if(mx == -1 || sz[v] > sz[mx]) mx = v;
    }
    if(sum == 0) return;
    if(sz[mx] - k <= sum - sz[mx]){
        res += (sum - k) / 2;
        return;
    }
    int cnt = sum - sz[mx];
    res += cnt;
    int nk = max(0, k + cnt - 1);
    dfs(mx, nk);
}

int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
    for(int i = 2; i <= n; i++){
        int p;
        cin >> p;
        g[p].push_back(i);
    }
    dfs_sz(1);
    res = 0;
    dfs(1, 0);
    cout << res << endl;
    return 0;
}

L3-1

https://ac.nowcoder.com/acm/contest/90070/E

L3-2

https://ac.nowcoder.com/acm/contest/86387/F

L3-3

https://ac.nowcoder.com/acm/contest/87656/E