第十五届CCPC山东省赛补题

L.恒星

思路

签到题,模拟即可。

代码

#include <bits/stdc++.h>
#define endl '\n'
using i64 = long long;
void solve(void) {
    std::string a, b;
    std::cin >> a >> b;
    std::unordered_map<char, int> M;
    M['O'] = 7;M['B'] = 6;M['A'] = 5;M['F'] = 4;
    M['G'] = 3;M['K'] = 2;M['M'] = 1;
    if(a == b) std::cout << "same" << endl;
    else {
        if(M[a[0]] != M[b[0]]) std::cout << (M[a[0]] > M[b[0]] ? "hotter" : "cooler") << endl;
        else std::cout <<  (a[1] < b[1] ? "hotter" : "cooler") << endl;
    }
}
int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
    int t = 1;
    std::cin >> t;
    while(t--) solve();
    return 0;
}

D.分布式系统

思路

模拟样例可以发现,对于每项任务,我们可以将它的所有子任务分成两部分。第一部分共有 \(\lfloor\frac{a_i}{n} \rfloor \times n\) 个,这些任务可以平均分配到 \(0\)\(n - 1\)\(n\) 个节点中;剩余的部分则是从 \(b_i\) 开始,依次往后分配,每个节点分配一个,如果分配到 \(n - 1\) 仍有剩余,则从 \(0\) 号节点开始第二轮。上面的分配过程我们可以用差分数组维护。

代码

#include <bits/stdc++.h>
#define endl '\n'
using i64 = long long;
void solve(void) {
    int n, q; std::cin >> n >> q;
    std::vector<i64> diff(n + 1);
    while(q--) {
        int a, b; std::cin >> a >> b;
        diff[0] += (a / n); diff[n] -= (a / n);
        int t = a % n;  // 剩下的部分
        if(t) {
            if(b + t <= n) {
                diff[b] += 1;
                diff[b + t] -= 1;
            } else {        //考虑第二轮
                diff[0] += 1;
                diff[n] -= 1;
                diff[b] += 1;
                diff[(b + t) % n] -= 1;
            }
        }
    }
    for(int i = 0; i < n; i++) {
        if(i != 0) diff[i] += diff[i - 1];
        std::cout << diff[i] << " ";
    }
    std::cout << endl;
}
int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
    int t = 1;
    std::cin >> t;
    while(t--) solve();
    return 0;
}

G.装配线

思路

对于每个个工件,它在 \(t_i\) 时刻达到 \(w_i\) 号工人,之后还要经过 \(k - w_i\) 个工人,所以理想情况下可以在 \(t_i + k - w_i\) 时刻完成这个工件。但是由于所有工件处在同一个队列中,所以有时当前工件需要等到前一个工件完成后才能被轮到。设每个工件的理想时刻为 \(f_i\) ,按照升序排列,则每个工件完成的时间为:

\[f_i = \max(f_i, f_{i - 1} + 1) \]

代码

#include <bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
using i64 = long long;
typedef std::pair<int, int> PII;
void solve(void) {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> t(n), w(n);
    for(int i =0; i < n; i++) std::cin >> w[i] >> t[i];
    std::vector<int> f(n);
    for(int i = 0; i < n; i++) f[i] = t[i] + k - w[i];
    sort(f.begin(), f.end());
    for(int i = 1; i < n; i++) f[i] = std::max(f[i], f[i - 1] + 1);
    std::cout << f[n - 1] << endl;
}
int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
    int t = 1;
    std::cin >> t;
    while(t--) solve();
    return 0;
}

A.项目管理

思路

一种可行的贪心思路是将所有的员工按照职级作为第一关键字,按照能够接受的人数作为第二关键字作为升序排序。之后维护一个队列,倒着遍历所有的员工,如果遇到相同职级的就直接加入到队列中,如果之后插入的员工的等级小于队列中的一些员工,并且队列中员工的数量超过了他的接受范围,那么就从队头出队直到剩下的人数让他可以接受。容易发现在贪心的过程中职级高的人越少越容易满足要求,因此倒着遍历可以不重不漏的考虑每一个员工且尽可能的选择更多的员工,遍历到最低级的员工时一定可以得到最优的答案。

代码

#include <bits/stdc++.h>
#define endl '\n'
using i64 = long long;
struct st {
    int x, y, idx;
};
void solve(void) { 
    int n; std::cin >> n;
    std::vector<st> a(n);
    for(int i = 0; i < n; i++) {
        std::cin >> a[i].x >> a[i].y;
        a[i].idx = i + 1;
    }
    sort(a.begin(), a.end(), [](const st &x, const st &y) {
        if(x.x != y.x) return x.x < y.x;
        return x.y < y.y;
    });
    std::queue<int> ans;
    ans.push(a[n - 1].idx);
    int cnt1 = 1, cnt2 = 1;
    for(int i = n - 2; i >= 0; i--) {
        if(a[i].x != a[i + 1].x) {
            cnt1 = 1;
            cnt2 = 0;
        } else cnt1++;
        int siz = ans.size();
        int st = siz - cnt2;
        if(cnt1 + std::min(st, a[i].y) > siz) {
            int add = cnt1 - cnt2;
            for(int j = 0; j < add && i + j < n; j++) {
                ans.push(a[i + j].idx);
                cnt2++;
            }
            siz = ans.size();
            st = siz - cnt2;
            if(st > a[i].y) {
                int t = st - a[i].y;
                while(t-- > 0) ans.pop();
            }
        }
    }
    std::cout << ans.size() << endl;
    while(ans.size()) {
        std::cout << ans.front() << " ";
        ans.pop();
    }
    std::cout << endl;
}
int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
    int t = 1;
    std::cin >> t;
    while(t--) solve();
    return 0;
}

I.方阵谜题

思路

本质上比较经典的 BFS 搜状态(类似于八数码),难点在于操作比较多,并且对于每组数据,起始和结束状态是不唯一的。第一个问题考验的是选手的代码能力,需要依次进行模拟;第二个问题我们可以直接以 "123456789" 这个状态为起点进行搜索。只需要一次预处理后就能得到所有的状态的最小步数。之后对于每组数据,将每组数据的输入输出与初始状态进行映射,就可以得到答案了。

代码

#include <bits/stdc++.h>
#define endl '\n'
using i64 = long long;
std::unordered_map<std::string, int> dis;
std::unordered_map<std::string, int> vis;
void bfs(void) {
    std::queue<std::string> q; q.push("123456789");
    dis["123456789"] = 0; vis["123456789"] = 1;
    while(q.size()) {
        auto u = q.front(); q.pop();
        std::string ss = u;
        char t = ss[2]; ss[2] = ss[1]; ss[1] = ss[0]; ss[0] = t;
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
        ss = u;
        t = ss[5]; ss[5] = ss[4]; ss[4] = ss[3]; ss[3] = t;
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
        ss = u;
        t = ss[8]; ss[8] = ss[7]; ss[7] = ss[6]; ss[6] = t;
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
        ss = u;
        t = ss[6]; ss[6] = ss[3]; ss[3] = ss[0]; ss[0] = t;
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
        ss = u;
        t = ss[7]; ss[7] = ss[4]; ss[4] = ss[1]; ss[1] = t;
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
        ss = u;
        t = ss[8]; ss[8] = ss[5]; ss[5] = ss[2]; ss[2] = t;
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
        ss = u;
        ss[0] = u[6]; ss[1] = u[3]; ss[2] = u[0]; ss[3] = u[7];
        ss[5] = u[1]; ss[6] = u[8]; ss[7] = u[5]; ss[8] = u[2];
        if(!vis[ss]) {
            vis[ss] = 1;
            dis[ss] = dis[u] + 1;
            q.push(ss);
        }
    }
}
void solve(void) { 
    std::string A = "", B = "", tmp;
    std::unordered_map<char, char> num;
    for(int i = 0; i < 3; i++) {
        std::cin >> tmp;
        A += tmp;
    }
    for(int i = 0; i < 3; i++) {
        std::cin >> tmp;
        B += tmp;
    }
    //std::cout << A << endl << B << endl;
    for(int i = 0; i < 9; i++) {
       num[A[i]] = char(i + '1');
    }
    std::string state = "";
    for(int i = 0; i < 9; i++) {
        state += num[B[i]];
    }
    //std::cout << state << endl;
    if(vis.count(state) == 0) {
        std::cout << -1 << endl;
        return;
    }
    std::cout << dis[state] << endl;
}
int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
    bfs();
    int t = 1;
    std::cin >> t;
    while(t--) solve();
    return 0;
}

H.最小生成树

思路

先对原图求最小生成树,然后删去树上前 \(k\) 大的边,此时树就变成了若干个联通快。我们可以枚举每两个相邻的点,如果这两个点不连通那么就在两个点之间建立一条边,这条边的边权显然是 \(1\) 。最后构造出来的新的生成树一定是边权最小,因为我们把树中边权最大的 \(k\) 个边都转换为了边权最小的边。

代码

#include <bits/stdc++.h>
#define x first
#define y second
#define endl '\n'
using i64 = long long;
const int N = 2e5 + 10;
struct Edge {
    int u, v, w, idx;
};
typedef std::pair<int ,int> PII;
int p[N];
int find(int x) {
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
bool unite(int a, int b) {
    a = find(a), b = find(b);
    if(a == b) return false;
    p[a] = b;
    return true;
}
void solve(void) { 
    int n, m, k; std::cin >> n >> m >> k;
    int res = 0;
    std::vector<Edge> e(m);
    for(int i = 1; i <= n; i++) p[i] = i;
    for(int i = 0; i < m; i++) {
        std::cin >> e[i].u >> e[i].v >> e[i].w;
        e[i].idx = i + 1;
    }
    sort(e.begin(), e.end(), [](const Edge &a, const Edge &b) {
        return a.w < b.w;
    });
    std::vector<Edge> mst;
    mst.reserve(n - 1);
    for(auto &ed : e) {
        if(unite(ed.u, ed.v)) {
            mst.push_back(ed);
            if((int)mst.size() == n - 1) break;
        }
    }
    std::vector<int> ord(n - 1);
    iota(ord.begin(), ord.end(), 0);
    sort(ord.begin(), ord.end(), [&](const int x, const int y) {
        return mst[x].w > mst[y].w;
    });
    std::vector<bool> st(n - 1, false);
    int cnt = 0;
    for(auto i : ord) {
        if(cnt < k && mst[i].w > 1) {
            cnt++;
            st[i] = true;
        }
    }
    for(int i = 1; i <= n; i++) p[i] = i;
    std::vector<int> keep_idx;
    keep_idx.reserve(n - 1);
    i64 total = 0;
    for(int i = 0; i < n - 1; i++) {
        if(!st[i]) {
            unite(mst[i].u, mst[i].v);
            keep_idx.push_back(mst[i].idx);
            total += mst[i].w;
        }
    }
    std::vector<PII> add;
    add.reserve(cnt);
    for(int i = 1; i < n && (int)add.size() < cnt; i++) {
        if(find(i) != find(i + 1)) {
            unite(i, i + 1);
            add.emplace_back(i, i + 1);
            total += 1;
        }
    }
    std::cout << cnt << endl;
    for(auto i : add) {
        std::cout << i.x << " " << i.y << endl;
    }
    std::cout << total << endl;
    for(auto i : keep_idx) std::cout << i << " ";
    for(int i = 0; i < cnt; i++)  {
        std::cout << (m + i + 1) << " ";
    }
    std::cout << endl;
}
int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr); std::cout.tie(nullptr);
    int t = 1;
    std::cin >> t;
    while(t--) solve();
    return 0;
}
posted @ 2025-06-05 17:08  dbywsc  阅读(306)  评论(0)    收藏  举报