2024.12.17 CW 模拟赛

题面 & 题解

T1

算法

背包, 动态规划.

思路

考虑到 \(w_i \le 1000\), 可以得到状态 \(f_{i,j}\) 表示到第 i 个, 已经有 j 能力的最大精彩度.

然后就是正常 01 背包了.

最后将两序列相同的 j 合并, 最后取最大值即可.

时间复杂度 \(\mathcal{O}(n^3)\). (真的可以过!)

#include "bits/stdc++.h"

using namespace std;

constexpr int N = 1e3 + 1;

#define int long long

int n, m, suma = 0, sumb = 0;
struct Node {
    int w, v;
} a[N], b[N];

void init() {
    mt19937_64 rnd(time(0));
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i].w >> a[i].v, suma += a[i].w;
    shuffle(a + 1, a + n + 1, rnd);
    for (int i = 1; i <= m; ++i)
        cin >> b[i].w >> b[i].v, sumb += b[i].w;
    shuffle(b + 1, b + m + 1 , rnd);
}

int f[N * N], g[N * N];

void calculate() {
    memset(f, 128, sizeof f);
    memset(g, 128, sizeof g);
    f[0] = g[0] = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = suma; j >= a[i].w; --j)
            f[j] = max(f[j], f[j - a[i].w] + a[i].v);
    for (int i = 1; i <= m; ++i)
        for (int j = sumb; j >= b[i].w; --j)
            g[j] = max(g[j], g[j - b[i].w] + b[i].v);
    int ans = -1e18;
    for (int i = min(suma, sumb); i; --i)
        if (f[i] >= -1e9 and g[i] >= -1e9)
            ans = max(ans, f[i] + g[i]);
    if (ans < 0)
        ans = 0;
    cout << ans << '\n';
}

void solve() {
    init();
    calculate();
}

signed main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
    return 0;
}

T2

算法

组合数学.

思路

对每一条边进行考虑.

假定 1 号点为根, 每个点的子树大小为 \(x\).

对于某一条边, \(m\) 个点一定分布在它的左右两端, 因为要使得代价最小,

所以一定是点数小的一边向点数大的一边走, 那么这条边就会被经过 \(\min(m-i,\ i)\) 次.

于是, 对于每一条边都有以下式子:

\[f(x) = \sum_{i = 1}^m \min(i,\ m - i) \cdot \tbinom{x}{i} \cdot \tbinom{n - x}{m - i} \]

最后答案即 \(\sum f(\)子树大小\()\).

这个 \(\min\) 比较麻烦, 拆开一下:

\[f(x) = \sum_{i = 1}^{\frac{m - 1}{2}} i \cdot \tbinom{x}{i} \cdot \tbinom{n - x}{m - i} + \sum_{i = \frac{m - 1}{2} + 1}^{m} (m - i) \cdot \tbinom{x}{i} \cdot \tbinom{n - x}{m - i} \]

\(t = \frac{m - 1}{2}\).
先处理左半部分:

\[g(x) = \sum^t_{i = 1} i \cdot \tbinom{x}{i} \cdot \tbinom{n - x}{m - i} = \sum^t_{i = 1} x \cdot \tbinom{x - 1}{i - 1} \cdot \tbinom{n - x}{m - i} \]

\[\leftrightarrow \frac{g(x)}{x} = \sum^t_{i = 1} \tbinom{x - 1}{i - 1} \cdot \tbinom{n - x}{m - i} \]

这样就可以 \(\mathcal{O}(n)\) 递推 \(s(x)\), 进而处理 \(g(x)\) 了.

#include "iostream"

using namespace std;

constexpr int N = 2e6 + 10, mod = 1e9 + 7;

int n, m;
int inv[N], fac[N];
int sz[N], fa[N];

void init() {
    cin >> n >> m;
    sz[1] = 1;
    for (int v = 2; v <= n; ++v)
        cin >> fa[v], sz[v] = 1;
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i <= 1e6; ++i) {
        fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
    }
    for (int i = 2; i <= 1e6; ++i)
        inv[i] = 1ll * inv[i] * inv[i - 1] % mod;
}

#define get(x, y) (1ll * fac[(y)] * (1ll * inv[(y) - (x)] * inv[(x)] % mod) % mod)

int f[N];

void calculate() {
    int t = m - 1 >> 1, ans = 0;
    f[1] = (!t ? 0 : get(m - 1, n - 1));
    for (int i = 2; i ^ n; ++i)
        f[i] = (mod + f[i - 1] - get(t - 1, i - 2) * get(m - t - 1, n - i) % mod) % mod;
    for (int i = 1; i ^ n; ++i)
        f[i] = 1ll * f[i] * i % mod;
    for (int i = 1; i <= n - i; ++i) {
        f[i] = f[n - i] = (f[i] + f[n - i]) % mod;
        if (!(m & 1))
            f[i] = f[n - i] = (f[i] + 1ll * get(t + 1, i) * get(t + 1, n - i) % mod * (t + 1) % mod) % mod;
    }
    for (int i = n; i ^ 1; --i) {
        sz[fa[i]] += sz[i];
        ans = (ans + f[sz[i]]) % mod;
    }
    cout << ans << '\n';
}

void solve() {
    init();
    calculate();
}

int main() {
    solve();
    return 0;
}
posted @   Steven1013  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示