Loading

2.10 CW 练时记录

前言

把之前的题补了一下, 还可以跟榜走, 太棒啦

\(\rm{B}\)

不好是概率与期望

思路

给定 \(n\)\(T_i, P_i\) , 要求一个顺序 \(a_1, a_2, a_3, \cdots , a_k\) , 使得对应的 \(EP = P_1 + P_2 + \ldots + P_k\) 最大, 相同 \(EP\) 应当使得 \(ET = T_1P_1 + (T_1 + T_2)P_2 + \ldots + (T_1 + T_2 + \ldots + T_k)P_k\) 最小, 输出最优的 \(a\)


不难猜想对于一组 \(EP\) 的最大值, 按照 \(T\) 为第一关键字升序, \(P\) 为第二关键字降序排序之后 \(ET\) 一定最小
考虑这个结论是否正确

反证法, 假设交换上述排序后的一对 \(P\) , 贡献之差是

\[\begin{align*} & T_i P_i + T_j P_j - T_i P_j - T_j P_i \\ =& \ T_i (P_i - P_j) + T_j (P_j - P_i) \\ =& \ (T_i - T_j)(P_i - P_j) < 0 \end{align*}\]

感觉不太好

或者说我们拆 \(ET\)

\[\begin{align*} ET &= T_1P_1 + (T_1 + T_2)P_2 + \ldots + (T_1 + T_2 + \ldots + T_k)P_k \\ &= \sum_{i = 1}^{k} \left(T_i \times \sum_{j = i}^{k} P_j\right) \\ &= \sum_{i = 1}^{k} \left[T_i \times \left(n - \sum_{j = 1}^{i - 1} P_j\right)\right] \\ &= n\sum_{i = 1}^{k} T_i - \sum_{i = 1}^{k} \left(T_i \times \sum_{j = 1}^{i - 1} P_j\right) \\ \end{align*}\]

那感觉很对啊, 不管了, 大不了最后寄了再说


我们先对于每一组 \(\{T_i, P_i\}\) 排序, 然后这样子选取一定是按顺序选

如果没有不同选择方案相同 \(EP\) , 那么就是一个简单的背包问题, 但是我们还要考虑 \(ET\)
猜测对于排序之后的 \(\{T_i, P_i\}\) , 我们可以有一个贪心策略

这个时候发现时空复杂度剩的很多, 可能可以对状态进行处理, 使其更好转移

考虑这样设计状态
\(f_{i, j, W}\) 表示考虑到了第 \(i\) 组, 当前的 \(EP\)\(j\) , 当前使用的 \(T\)\(W\) 时, 最小的 \(ET\)

但是时间复杂度爆炸了

不会丢了, 一会再说


好的跟榜, 这题过得人多
主要原因还是懒, 不想开新题

考虑「不同选择方案相同 \(EP\)」时, 怎样才能让 \(ET\) 最小, 加状态被证明是完蛋的

发现要输出具体方案, 于是肯定要记录 \(last\) 回溯
不难想到对于每一种「相同 \(EP\)」, 把所有可能的回溯全部记下来, 最后全部跑一遍看哪种方案的 \(ET\) 最优秀

不好实现啊, 丢了

发现 \(ET\) 可以贪心于是可以直接转移, 下午看正解不浪费时间

\(\rm{D}\)

跟榜

思路

好像就是分拆数?

\[p(0, 0) = 1 \\ p(n, k) \gets p(n - 1, k - 1) + p(n - k, k) \]

但是可能要写高精度, \(\rm{qcz}\) 大佬说 \(\textrm{\_\_int128}\) 能过, 试一下

代码

#include <bits/stdc++.h>
const int MAXN = 1520;

__int128 p[2][MAXN];
int now = 0, nxt = 1;

void write(__int128 x)
{
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

int main()
{
    freopen("sweets.in", "r", stdin);
    freopen("sweets.out", "w", stdout);
    int n, k;
    scanf("%d %d", &n, &k);
    memset(p, 0, sizeof p);
    p[now][0] = 1;
    for (int i = 1; i <= k; i++) {
        std::swap(now, nxt);
        memset(p[now], 0, sizeof p[now]);
        for (int j = 1; j <= n; j++) {
            if (i > j) continue;
            p[now][j] += p[nxt][j - 1] + p[now][j - i];
        }
    }
    write(p[now][n]);
    return 0;
}

\(\rm{F}\)

跟榜, 看完这道题差不多可以去补昨天的题

话说昨天 \(\rm{C}\) 确实挺聪明的
发现自己理解是假的

思路

吃饭

posted @ 2025-02-10 14:20  Yorg  阅读(21)  评论(0)    收藏  举报