Codeforces Round #748 (Div. 3) 解题报告

CF1593A Elections

题意

三个人参加投票选举,分别得到了 \(a\) 票、 \(b\) 票和 \(c\) 票。求每个人至少还要得到多少票才能比另外两个人得到的票都多。(每个人需要多得到的票数独立计算,不会互相影响。)

思路

对于第一个人,检查 \(a\) 是否大于 \(\max\{b, c\}\),如果大于就不需要多得到投票,否则需要多得到 \(\max\{b, c\} - a + 1\) 票。(之所以要 +1 是因为必须比别人得票多而不是和别人得票相等。)

第二个人、第三个人同理。

代码

#include<iostream>
#include<algorithm>
using namespace std;

int main() {
    int a, b, c, T;

    cin >> T;
    while(T--) {
        cin >> a >> b >> c;

        if(a > max(b, c)) {
            cout << 0 << " ";
        } else {
            cout << max(b, c) - a + 1 << " ";
        }
        if(b > max(a, c)) {
            cout << 0 << " ";
        } else {
            cout << max(a, c) - b + 1 << " ";
        }
        if(c > max(b, a)) {
            cout << 0 << " ";
        } else {
            cout << max(b, a) - c + 1 << " ";
        }
        cout << endl;
    }
    return 0;
}

CF1593B Make it Divisible by 25

题意

输入一个正整数 \(n\),删去若干位数使其能被 25 整除,求最少要删去多少位数字。

数据范围:\(25 \leq n \leq 10^{18}\)

思路

25 的倍数的特征很简单:末尾两位数是 00、25、50、75,所以我们可以考虑进行贪心:

从最后一位出发,找到最低位的 0 和最低位的 5,记录为第 \(p\) 位和第 \(q\) 位。

然后再从第 \(p\) 位出发,向左找到遇到的第一个 0 或 5,将这两位后面的所有数都删去,更新答案;从第 \(q\) 位出发,向左找到遇到的第一个 2 或 7,将这两位后面的所有数都删去,更新答案。

代码

代码实现比较简单,但要注意细节。

#include<iostream>
using namespace std;

const int MAXN = 100 + 5;

int a[MAXN];
int len = 0;

int main() {
    int T;

    cin >> T;
    while(T--) {
        long long n;

        cin >> n;
        len = 0;
        long long t = n;
        while(t != 0) {
            a[++len] = t % 10;
            t /= 10;
        }
        int p = len, q = len, ans = len;
        for(int i = len; i >= 1; i--) {
            if(a[i] == 0) {
                p = i;
            } else if(a[i] == 5) {
                q = i;
            }
        }
        for(int i = p + 1; i <= len; i++) {
            if(a[i] == 0 || a[i] == 5) {
                ans = min(ans, i - 2);
            }
        }
        for(int i = q + 1; i <= len; i++) {
            if(a[i] == 2 || a[i] == 7) {
                ans = min(ans, i - 2);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

CF1593C Save More Mice

题意

在数轴上有一只猫,\(k\) 只老鼠和一个洞,猫在点 \(0\),洞在点 \(n\),第 \(i\) 只老鼠在点 \(x_i (0 < x_i < n)\)

每个时刻有且仅有一只老鼠可以向右移动一位,猫也会向右移动一位。如果猫的位置和一只老鼠重合,猫就会吃掉这只老鼠,已经在洞的位置上的老鼠不会被吃掉。

求最多有多少只老鼠可以不被猫吃掉。

思路

这道题的解法是贪心。

现将所有老鼠按照坐标排序,再依次从第 \(n\) 个老鼠开始,从右到左依次尝试将每一个老鼠移动到洞里,如果当前的老鼠已经被吃掉了就停止(所有老鼠已经不是到了洞里就是被吃掉)。

如果不移动最右侧的老鼠而是移动其他老鼠,最右侧老鼠不动而猫向右移动,猫与所有老鼠构成的区间长度缩短,答案显然不会更优。

代码

#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 400000 + 5;

int a[MAXN];
int n, k;

int main() {
    int T;

    cin >> T;
    while(T--) {
        cin >> n >> k;
        for(int i = 1; i <= k; i++) {
            cin >> a[i];
        }
        sort(a + 1, a + k + 1);
        int cat = 0, ans = 0;
        for(int i = k; i >= 1; i--) {
            if(cat >= a[i]) {
                break;
            }
            int dis = n - a[i];
            ans++;
            cat += dis;
        }
        cout << ans << endl;
    }

    return 0;
}

CF1593D1 All are Same

题意

输入 \(n\) 个数 \(a_1,a_2,...a_n\),每次操作可以将一个数减小 \(k\),若干次操作后所有的数的值全部相同,求 \(k\) 的最大值。如果 \(k\) 可以任意大,输出 \(-1\)

思路

将一个数 \(a\) 不断减去相同的数 \(k\) 之后,变为另一个数 \(b\)\(k\) 显然是 \(|b - a|\) 的约数,拓展到 \(n\) 个数后同样成立。

所以题目中所求的 \(k\) 就是 \(\gcd(a_1 - a_2, a_2 - a_3, a_3 - a_4,... a_{n - 1} - a_n)\),即对差分求最大公约数。

代码

#include<iostream>
#include<fstream>
#include<algorithm>
using namespace std;

const int MAXN = 40 + 5;

int a[MAXN];
int n;

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

int main() {
    int T;

    cin >> T;
    while(T--) {
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        int ans = 0;
        for(int i = 2; i <= n; i++) {
            ans = gcd(ans, a[i] - a[i - 1]);
        }
        if(ans < 0) {
            ans = -ans;
        }
        if(ans == 0) {
            cout << -1 << endl;
        } else {
            cout << ans << endl;
        }
    }
    return 0;
}

CF1593E Gardener and Tree

题意

对一棵 \(n\) 个节点的树进行 \(k\) 次操作,每次操作删去当前树的所有叶结点,求 \(k\) 次操作后树还剩下多少个节点。

思路

注意到每个节点“在哪一次操作中被删去”的属性有明显的先后顺序,所以我们可以对这棵树进行一个拓扑排序。

定义每个节点“在哪一次操作中被删去”的值为 \(rnk_i\)。每次检查到 \(deg_v = 1\) 的节点 \(v\) 就将其入队,同时用“引荐”它入队的节点 \(u\) 计算 \(v\)\(rnk\) 值,\(rnk_v = rnk_u + 1\)

最后检查哪些节点的 \(rnk\) 值小于 \(k\),这些节点会被剪掉,其他节点的数量就是答案。

代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;

const int MAXN = 400000 + 5;

vector<int> G[MAXN];
int deg[MAXN], rnk[MAXN];
int n, k;

void toposort() {
    queue<int> q;
    for(int i = 1; i <= n; i++) {
        if(deg[i] == 1) {
            q.push(i);
            rnk[i] = 1;
        }
    }
    while(!q.empty()) {
        int u = q.front(); q.pop();

        for(int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];

            if(--deg[v] == 1) {
                rnk[v] = rnk[u] + 1;
                q.push(v);
            }
        }
    }
}

int main() {
    int T;

    cin >> T;
    while(T--) {
        memset(deg, 0, sizeof(deg));
        memset(rnk, 0, sizeof(rnk));

        cin >> n >> k;
        for(int i = 1; i <= n; i++) {
            G[i].clear();
        }
        for(int i = 1; i <= n - 1; i++) {
            int u, v;

            cin >> u >> v;
            G[u].push_back(v);
            G[v].push_back(u);
            deg[u]++; deg[v]++;
        }
        toposort();
        int ans = n;
        for(int i = 1; i <= n; i++) {
            if(rnk[i] <= k) {
                ans--;
            }
        }
        cout << ans << endl;
    }
    return 0;
}
posted @ 2021-12-05 19:09  智子酱  阅读(52)  评论(0编辑  收藏  举报