ICPC 2025 南昌邀请赛

A. Nezha Naohai

题目大意

有三件事,分别要花费A,B,C 小时。每个小时会累计D条的抱怨。请问一共累计了多少条抱怨。

解题思路

输出(A+B+C)*D即可

代码实现

#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 a, b, c, d;
    std::cin >> a >> b >> c >> d;
    std::cout << (a + b + c) * d << "\n";
}

F. Caloric Difference

题目大意

规划接下来n天的热量差,假设第i天摄入的热量为\(c_i\),则第i天消耗的热量为\(b_i=pb_{i−1}+(1−p)c_{i−1}\),给出\(b_0,c_0\)和p。有k个位置的\(c_i\)为固定值,表示要吃放纵餐和每个\(c_i\)必须在全局给出的[L,R]之间选择。问n天内最大的(总热量消耗-总热量摄入)值。

解题思路

考虑每个\(c_i\)的贡献,化简后为\(-c_ip^{n-i}\)\(c_i\)越小,答案越大,因此没固定的\(r_i\)全取L即可,注意浮点数

代码实现

#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, k;
        std::cin >> n >> k;

        double r0, c0, p, L, R;
        std::vector<double> r(n + 1), c(n + 1);
        std::cin >> r[0] >> c[0] >> p >> L >> R;

        for (int i = 1; i <= k; i++) {
            int p;
            double v;
            std::cin >> p >> v;
            r[p] = v;
        }

        double ans = 0;
        for (int i = 1; i <= n; i++) {
            if (r[i] == 0.0) {
                r[i] = L;
            }
            c[i] = p * c[i - 1] + (1 - p) * r[i - 1];
            ans += c[i] - r[i];
        }

        std::cout << std::fixed << std::setprecision(6) << ans << "\n";
    }
}

D. Virtuous Pope

题目大意

给定三维空间内的若干条线段,限制其端点在一给定长方体上,求对于任意与坐标轴垂直的平面最多能和多少条线段相交。

解题思路

把每个线段分别对xyz平面做映射,就把问题转化为了求数轴最大重叠线段数量

代码实现

#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, a, b, c;
    std::cin >> n >> a >> b >> c;

    std::vector<std::array<int, 2>> x, y, z;
    for (int i = 0; i < n; i++) {
        int x1, y1, z1, x2, y2, z2;
        std::cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
        x.push_back({std::min(x1, x2), 1});
        x.push_back({std::max(x1, x2), -1});
        y.push_back({std::min(y1, y2), 1});
        y.push_back({std::max(y1, y2), -1});
        z.push_back({std::min(z1, z2), 1});
        z.push_back({std::max(z1, z2), -1});
    }
    std::sort(x.begin(), x.end(), [&](auto a, auto b) {
        if (a[0] != b[0]) {
            return a[0] < b[0];
        }
        return a[1] > b[1];
    });
    std::sort(y.begin(), y.end(), [&](auto a, auto b) {
        if (a[0] != b[0]) {
            return a[0] < b[0];
        }
        return a[1] > b[1];
    });
    std::sort(z.begin(), z.end(), [&](auto a, auto b) {
        if (a[0] != b[0]) {
            return a[0] < b[0];
        }
        return a[1] > b[1];
    });

    int ans = 0, X = 0, Y = 0, Z = 0;
    for (int i = 0; i < 2 * n; i++) {
        X += x[i][1];
        Y += y[i][1];
        Z += z[i][1];
        ans = std::max({ans, X, Y, Z});
    }

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

K. Rotation

题目大意

有n个雕像,雕像朝向4个方向:前方、右方、后方或左方。
每次你都可以进行以下一种操作

  • 选一个雕像不动,其他所有雕像同时顺时针旋转90度
  • 使所有雕像顺时针旋转90度

问要使所有雕像面向前方最少需要操作多少次

解题思路

可以发现,按某个雕像转一次再按住自己转3次就等于往反方向转了一次,先转到同向,然后枚举2操作即可

代码实现

#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<int> a(n + 1), b(4);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= 3; j++) {
            b[j] += (a[i] - j + 4) % 4;
        }
    }

    int ans = INT_MAX;
    for (int i = 0; i <= 3; i++) {
        ans = std::min(ans, b[i] + (b[i] * 3 + 4 - i) % 4);
    }

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

M. Divide coins

题目大意

有n枚硬币,其中k枚正面朝下,但你不知道是哪k枚。你可以翻转硬
币,问如何操作能将硬币分成两组,让两组硬币正面朝上的一样多。

解题思路

第一堆选k枚,第二堆选n−k枚,然后把第二堆全部翻转即可。
假设第一堆中有x个朝下了,则有k-x个朝上,第二堆有\(n-k-(k-x)=n-2*k+x\)个朝上,翻转第二堆则有\(n-k-(n-2*k+x)=k-x\)个朝上

代码实现

#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, k;
    std::cin >> n >> k;
    std::cout << std::string(k, '1') + std::string(n - k, '4') << "\n";
}

G. Exploration

题目大意

有向有权图,每次给起始点p,起始体力x,通过一条边权为d边后,体力变为\(\lfloor \frac{p}{x} \rfloor\),多次询问,求每次将体力消耗完要经过的最小步数。

解题思路

因为\(d \geq 2\)\(x \leq 10^9\),所以这条路不会特别长,考虑设计状态dp[i][j]为在j出发i步之后的最小乘积,暴力更新所有可行边即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

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

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

    std::vector<std::vector<i64>> dp(31, std::vector<i64>(n + 1, 1));
    for (int i = 1; i <= 30; i++) {
        for (int j = 1; j <= n; j++) {
            for (auto [u, d] : g[j]) {
                dp[i][j] = std::max(dp[i][j], dp[i - 1][u] * d);
            }
        }
    }

    while (q--) {
        int p, x;
        std::cin >> p >> x;
        for (int i = 1; i <= 30; i++) {
            if (dp[i][p] > x) {
                std::cout << i << "\n";
                break;
            }
        }
    }
}

I. Dating Day

题目大意

给定一个长度为n的01串,可以对一个1的数量为k的区间进行一次任意排序,问能得到多少种不同的字符串,对答案模998244353

解题思路

CF1622D,记录每个1的位置,枚举所有包含k个1的最大窗口len1,累加C(len1,k),对于相邻的窗口会有k-1个1重叠,重叠窗口大小len2,x需要减去重叠的C(len2,k-1),等价于减去所有k-1个1的最大窗口len2,去掉最左和最右窗口

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int MOD = 998244353;
const int N = 1e5 + 10;

std::vector<i64> fac(N + 1, 1), invfac(N + 1, 1);

i64 ksm(i64 a, i64 n, i64 mod) {
    i64 res = 1;
    a = (a % mod + mod) % mod;
    while (n) {
        if (n & 1) {
            res = (a * res) % mod;
        }
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}

void init(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
    }
    invfac[n] = ksm(fac[n], MOD - 2, MOD);
    for (int i = n - 1; i >= 0; i--) {
        invfac[i] = invfac[i + 1] * (i + 1) % MOD;
    }
}

i64 C(int n, int m) {  // 组合数
    if (m > n || m < 0) {
        return 0;
    }
    return fac[n] * invfac[m] % MOD * invfac[n - m] % MOD;
}

i64 A(int n, int m) {  // 排列数
    if (m > n || m < 0) {
        return 0;
    }
    return fac[n] * invfac[n - m] % MOD;
}

// n 对括号的合法匹配数,有 n 个节点的二叉树的种类数
// 从对角线下方走到对角线的路径数,栈的出栈序列数
i64 catalan(int n) {  // 卡特兰数
    if (n < 0) {
        return 0;
    }
    return C(2 * n, n) * ksm(n + 1, MOD - 2, MOD) % MOD;
}

// 将 n 个不同的元素划分到 k 个非空集合中的方案数
i64 stirling2(int n, int k) {  // 第二类斯特林数
    if (k > n || k < 0) {
        return 0;
    }
    i64 res = 0;
    for (int i = 0; i <= k; i++) {
        i64 term = C(k, i) * ksm(k - i, n, MOD) % MOD;
        if (i % 2 == 1) {
            term = (MOD - term) % MOD;
        }
        res = (res + term) % MOD;
    }
    return res * invfac[k] % MOD;
}

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

    init(N);

    int tt;
    std::cin >> tt;

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

        std::string s;
        std::cin >> s;

        std::vector<int> pos = {-1};
        for (int i = 0; i < n; i++) {
            if (s[i] == '1') {
                pos.push_back(i);
            }
        }
        pos.push_back(n);

        i64 ans = 0;
        for (int i = 0; i + k + 1 < pos.size(); i++) {
            ans = (ans + C(pos[i + k + 1] - pos[i] - 1, k)) % MOD;
        }
        for (int i = 1; i + k + 1 < pos.size(); i++) {
            ans = (ans - C(pos[i + k] - pos[i] - 1, k - 1) + MOD) % MOD;
        }

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

E. God's String on This Wonderful World

题目大意

给定一个字符串,问[l,r]有多少个子区间满足每个字符出现的次数都能被k整除

解题思路

问题可以转化为[l-1,r]中有多少个二元组(a,b)满足所有的字母都同余(这样说明区间ab中的字符都是k的倍数),时限3s,数据范围3e5,区间扩展收缩可以计算答案,考虑使用莫队解决

代码实现

#include <bits/stdc++.h>

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

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

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

    int size = std::sqrt(n);
    int bnum = ceil((double)n / size);

    std::vector<i64> a(n + 1), ch(26), cnt(N), block(n + 1);
    for (int i = 1; i <= bnum; i++) {
        for (int j = (i - 1) * size + 1; j <= i * size && j <= n; j++) {
            block[j] = i;
        }
    }

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

    std::vector<std::array<i64, 3>> ask(q);
    for (int i = 0; i < q; i++) {
        std::cin >> ask[i][0] >> ask[i][1];
        ask[i][0]--;
        ask[i][2] = i;
    }

    std::sort(ask.begin(), ask.end(), [&](std::array<i64, 3> a, std::array<i64, 3> b) {
        if (block[a[0]] != block[b[0]]) {
            return block[a[0]] < block[b[0]];
        }
        if (block[a[0]] & 1) {
            return a[1] < b[1];
        } else {
            return a[1] > b[1];
        }
    });

    i64 l = 1, r = 0, idx = 1, now = 0;
    std::map<std::vector<i64>, i64> mp;
    mp[ch] = 0;
    for (int i = 1; i <= n; i++) {
        ch[s[i] - 'a'] = (ch[s[i] - 'a'] + 1) % k;
        if (!mp.count(ch)) {
            mp[ch] = idx++;
        }
        a[i] = mp[ch];
    }

    std::vector<i64> ans(q);
    for (auto [L, R, idx] : ask) {
        while (l > L) {
            l--;
            now += cnt[a[l]];
            cnt[a[l]]++;
        }
        while (r < R) {
            r++;
            now += cnt[a[r]];
            cnt[a[r]]++;
        }
        while (l < L) {
            cnt[a[l]]--;
            now -= cnt[a[l]];
            l++;
        }
        while (r > R) {
            cnt[a[r]]--;
            now -= cnt[a[r]];
            r--;
        }
        ans[idx] = now;
    }

    for (int i = 0; i < q; i++) {
        std::cout << ans[i] << "\n";
    }
}
posted @ 2025-07-17 17:42  udiandianis  阅读(98)  评论(0)    收藏  举报