JSCPC 2025 江苏省赛

D. Spell Generation

题目大意

有一个按钮,按住\(2^x\)秒可以得到\(10^x\)的值,现在要得到数字n,问至少要按住多少秒

解题思路

按题意模拟即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        std::string s;
        std::cin >> s;
        std::reverse(s.begin(), s.end());

        i64 ans = 0;
        for (int i = 0; i < s.size(); i++) {
            ans += (s[i] - '0') * (1 << i);
        }

        std::cout << ans << "\n";
    }
}

F. Ranking Prediction

题目大意

给定某个队伍的比赛提交信息,问这个队伍封榜后至少过几题才能超过你们队,不可能超过则输出-1

解题思路

按题意模拟即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, a, b, s, penalty = 0;
        std::cin >> n >> a >> b >> s;

        std::map<std::string, int> ac, pd1;
        std::vector<std::tuple<std::string, int>> rj;
        for (int i = 0; i < s; i++) {
            int t;
            std::string p, v;
            std::cin >> t >> p >> v;

            if (v == "ac") {
                if (ac.count(p)) {
                    ac[p] = std::min(ac[p], t);
                } else {
                    ac[p] = t;
                }
            } else if (v == "pd" && !ac.count(p)) {
                if (pd1.count(p)) {
                    pd1[p] = std::min(pd1[p], t);
                } else {
                    pd1[p] = t;
                }
            } else {
                rj.push_back({p, t});
            }
        }

        std::map<std::string, int> rj_cnt;
        for (auto [p, t] : rj) {
            if (ac.count(p) && ac[p] > t) {
                rj_cnt[p]++;
            }
            if (!ac.count(p) && pd1.count(p) && pd1[p] > t) {
                rj_cnt[p]++;
            }
        }

        for (auto [p, t] : ac) {
            penalty += t + 20 * rj_cnt[p];
        }
        for (auto &[p, t] : pd1) {
            t += 20 * rj_cnt[p];
        }
        std::vector<std::tuple<int, std::string>> pd2;
        for (auto [p, t] : pd1) {
            pd2.push_back({t, p});
        }
        std::sort(pd2.begin(), pd2.end());

        if (ac.size() > a || ac.size() == a && penalty < b) {
            std::cout << 0 << "\n";
        } else {
            int f = 1, cnt = ac.size();
            for (auto [t, p] : pd2) {
                penalty += t;
                cnt++;
                if (cnt > a || cnt == a && penalty < b) {
                    std::cout << cnt - ac.size() << "\n";
                    f = 0;
                    break;
                }
            }

            if (f) {
                std::cout << -1 << "\n";
            }
        }
    }
}

J. Puzzle Competition

题目大意

给定一张n点m边有向图,每个节点都有点权,边都有边权,一开始所有节点和边都是锁定的,只有被激活的入边数量不少于点权时才能激活该节点,如果一个节点被激活,其所有的出边经过边权时间后会被激活,有一些特殊装置会在指定时间后激活指定节点,问某个节点被激活的最小时间,不可能则输出-1

解题思路

参考拓扑序,对于被激活的边都向连接的点输送1的能量,装置可以看做在指定时间后指定节点有无穷大的能量,优先队列维护即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const i64 INF = 4e18 + 10;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int n, m, k;
    std::cin >> n >> m >> k;

    std::priority_queue<std::array<i64, 3>, std::vector<std::array<i64, 3>>, std::greater<std::array<i64, 3>>> pq;
    std::vector<std::array<i64, 2>> ans(n + 1, {INF, 0});
    for (int i = 1; i <= n; i++) {
        std::cin >> ans[i][1];
        if (ans[i][1] == 0) {
            pq.push({0, i, INF});
        }
    }

    for (int i = 0; i < k; i++) {
        int t, sc;
        std::cin >> t >> sc;
        for (int j = 0; j < sc; j++) {
            int id;
            std::cin >> id;
            pq.push({t, id, INF});
        }
    }

    std::vector<std::vector<std::array<i64, 2>>> g(n + 1, std::vector<std::array<i64, 2>>());
    for (int i = 0; i < m; i++) {
        i64 u, v, w;
        std::cin >> u >> v >> w;
        g[u].push_back({v, w});
    }

    std::vector<int> vis(n + 1);
    while (!pq.empty()) {
        auto [t, u, x] = pq.top();
        pq.pop();
        if (vis[u]) {
            continue;
        }
        ans[u][1] -= x;
        if (ans[u][1] <= 0) {
            vis[u] = 1;
            ans[u][0] = t;
            for (auto [v, w] : g[u]) {
                if (!vis[v]) {
                    pq.push({t + w, v, 1});
                }
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        if (ans[i][0] == INF) {
            std::cout << -1 << " \n"[i == n];
        } else {
            std::cout << ans[i][0] << " \n"[i == n];
        }
    }
}

G. Monetary System

题目大意

给定n张不同面额的货币,在选取面额尽可能大的情况下,问用m张货币能凑出多少种不一样的总金额

解题思路

已经给出f(x,y)的递推式,直接二分当前可用最大面额+记忆化搜索处理出f[i],之后再用前缀和来维护不超过x的金额能有多少种方案

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int N = 1e6 + 10;
const i64 INF = 4e18 + 10;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int n, q;
    std::cin >> n >> q;

    std::vector<i64> a(n + 2), b(N), f(N);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    a[n + 1] = INF;

    auto dfs = [&](auto &&self, int x, int y) -> i64 {
        if (x == 0) {
            return 0;
        } else if (y == 1) {
            return x;
        } else if (f[x]) {
            return f[x];
        } else {
            return x / a[y] + self(self, x % a[y], y - 1);
        }
    };

    for (int i = 1; i <= a[n]; i++) {
        f[i] = dfs(dfs, i, std::upper_bound(a.begin(), a.end(), i) - a.begin() - 1);
        b[f[i]]++;
    }
    for (int i = 1; i <= a[n]; i++) {
        b[i] += b[i - 1];
    }

    for (int i = 1; i <= q; i++) {
        i64 x;
        std::cin >> x;
        std::cout << b[std::min(x, a[n])] << " \n"[i == q];
    }
}

H. Loose Subsequences

题目大意

对经典的本质不同的子序列加上一个新的约束,子序列字母的间隔必须要相隔k以上,求这个条件下本质不同的子序列有多少,对答案模998244353

解题思路

k=0时是一个经典的不同本质子序列问题,可以用前缀和来维护,减掉重复贡献,不为0的时候对于上次出现的位置以及前缀和需要再往前k个

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int MOD = 998244353;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, k;
        std::cin >> n >> k;

        std::string s;
        std::cin >> s;
        s = " " + s;

        std::vector<i64> dp(n + 1), pre(n + 1);
        dp[0] = pre[0] = 1;
        std::map<char, int> last;
        for (int i = 1; i <= n; i++) {
            dp[i] = pre[std::max(i - k - 1, 0)];
            if (last.count(s[i])) {
                dp[i] = (dp[i] - pre[std::max(last[s[i]] - k - 1, 0)] + MOD) % MOD;
            }
            pre[i] = (pre[i - 1] + dp[i]) % MOD;
            last[s[i]] = i;
        }

        std::cout << (pre[n] - 1 + MOD) % MOD << "\n";
    }
}

I. Team Naming

题目大意

n个名字为三个字的人,问有多少三人队使得每个人名选一个不同位置的字组成的名字是队里的一个人名

解题思路

容斥+统计,详见注释

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int n;
    std::cin >> n;

    std::vector<std::array<int, 3>> s(n);
    std::map<int, int> mp0, mp1, mp2;
    std::map<std::array<int, 2>, int> mp01, mp02, mp12;
    for (int i = 0; i < n; i++) {
        std::cin >> s[i][0] >> s[i][1] >> s[i][2];
        mp0[s[i][0]]++;
        mp1[s[i][1]]++;
        mp2[s[i][2]]++;
        mp01[{s[i][0], s[i][1]}]++;
        mp02[{s[i][0], s[i][2]}]++;
        mp12[{s[i][1], s[i][2]}]++;
    }

    i64 ans = 0;
    for (int i = 0; i < n; i++) {                                                                                        // 每次都匹配第i个人的名字
        int s0 = mp0[s[i][0]] - 1, s1 = mp1[s[i][1]] - 1, s2 = mp2[s[i][2]] - 1;                                         // 在0,1,2位置能匹配上的人
        int s01 = mp01[{s[i][0], s[i][1]}] - 1, s02 = mp02[{s[i][0], s[i][2]}] - 1, s12 = mp12[{s[i][1], s[i][2]}] - 1;  // 在01,02,12位置能匹配上的人
        int x = s0 - s01 - s02, y = s1 - s01 - s12, z = s2 - s02 - s12;                                                  // 只在0,1,2位置能匹配上的人

        ans += x * y + x * z + y * z;                                            // jk只在一个位置上做贡献
        ans += (s01 + s02 + s12) * (x + y + z);                                  // jk其中一人在两个位置做贡献,另一人在一个位置做贡献
        ans += s01 * s02 + s01 * s12 + s02 * s12;                                // jk都在两个位置上做贡献,但是贡献的位置不同
        ans += s01 * (s01 - 1) / 2 + s02 * (s02 - 1) / 2 + s12 * (s12 - 1) / 2;  // jk都在两个位置上做贡献,但是贡献的位置相同
    }

    std::cout << ans << "\n";
}
posted @ 2025-07-14 20:32  udiandianis  阅读(285)  评论(0)    收藏  举报