Logo ryp 的博客

博客

EF 题解

...
ryp
2025-12-07 17:48:54
She's not square

E

观察数据范围,容易想到二分答案。

那么问题转化为如何统计 $1 \sim k$ 中的美丽数的数量。

考虑 $1 \sim k$ 中有多少个数能被 $p$ 整除,答案显然是 $\lfloor k/p \rfloor$。

考虑 $1 \sim k$ 中有多少个数能被 $p$ $q$ 整除,答案是 $\lfloor k/p \rfloor + \lfloor k/q \rfloor$ 吗?

不对,因为这样的话,能同时被 $p$ 和 $q$ 整除的数会被算两次。所以要减去能同时被 $p$ 和 $q$ 整除的数,也就是减去 $\lfloor k / \text{lcm}(p,q)\rfloor$。

考虑 $1 \sim k$ 中有多少个数能被 $p$ $q$ 整除,那么就是再减去能被 $p$ 和 $q$ 同时整除的数。

所以 $1 \sim k$ 中美丽数的数量就是 $\lfloor k / p \rfloor + \lfloor k / q \rfloor - 2\lfloor k/\text{lcm}(p,q)\rfloor$。

这样我们就可以直接二分得到最后的答案。

#include <iostream>
#define int long long
using namespace std;

int check (int p, int n, int m, int g)
{
    return p / n + p / m - p / g * 2;
}

int gcd (int a, int b)
{
    if (!b) return a;
    return gcd (b, a % b);
}

signed main (void)
{
    int t;


    cin.tie(0)->sync_with_stdio (false);
    cin >> t;
    while (t--) {
        int n, m, k;

        cin >> n >> m >> k;
        int g = n / gcd (n, m) * m;
        int l = 1, r = 1e18, mid, ans = -1;
        while (l <= r) {
            mid = (l + r) / 2;
            if (check (mid, n, m, g) < k) l = mid + 1;
            else r = (ans = mid) - 1;
        }
        cout << ans << '\n';
    }
    return 0;
}

F

考虑这其实和背包问题很像,只是本题有多个“价值”。

然而价值都很小(最大为 $6$),那么我们可以直接把它放到我们的 DP 状态里。

所以最终的状态就是:$f_{iabcdef}$,表示考虑了前 $i$ 种学习计划,每种科目的得分分别为 $a, b, c, d, e, f$。

其实还有更简单的写法:把 $abcdef$ 压到同一个整数里,也就是用一个六进制数来作为状态。

转移和背包问题是一样的。

#include <bits/stdc++.h>

using i64 = long long;

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

    int N, K, P;
    std::cin >> N >> P;
    K = 6;

    std::vector<int> pw(K + 1);
    pw[0] = 1;
    for (int i = 1; i <= K; i++) {
        pw[i] = pw[i - 1] * (P + 1);
    }

    std::vector dp(pw[K], -1LL);
    dp[0] = 0;

    for (int i = 0; i < N; i++) {
        int C;

        std::vector<int> A(K);
        for (int j = 0; j < K; j++) {
            std::cin >> A[j];
        }
        std::cin >> C;

        for (int s = pw[K] - 1; s >= 0; s--) {
            int t = 0;
            for (int j = 0; j < K; j++) {
                int a = s / pw[j] % (P + 1);
                int na = std::min(P, a + A[j]);
                t += na * pw[j];
            }
            if (dp[s] != -1 && (dp[t] == -1 || dp[t] > dp[s] + C)) {
                dp[t] = dp[s] + C;
            }
        }
    }
    std::cout << dp.back() << "\n";

    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。