Loading

2.20 CW 模拟赛 赛时记录

前言

状态真的不算好, 但是无所谓了, 按照策略做就行了

看题

给我干哪来了, 这是 \(\textrm{Day 2}\) ?

\(\rm{T1}\)

最好和期望没啥关系

\(\rm{T2}\)

不太好想

\(\rm{T3}\)

多半是数学题了


总而言之, 不贪跟策略, 数据检验很重要
身体很重要, 秋裤还是穿上吧

\(\rm{T1}\)

思路

题意

nn 个单元, 第 ii 个单元的权重为 wiw_i
每秒在余下的单元中, 以 wiw\displaystyle \frac{w_i}{\sum w} 的概率选择第 ii 个单元删除((wi0w_i \gets 0))

求第 11 个单元被删除时间的期望

时间上限为 \(n\) , 可以接受

不难想到 \(\rm{dp}\)
\(f_{i, j}\) 表示第 \(i\) 秒时 \(\sum w = j\) 的概率
什么你说 \(j\) 开不了, 超冰啊, 用 \(\rm{map}\) 即可

考虑转移

\[\textrm{let } f_{0, \sum w} = 1\\ f_{i, j} = \sum_{k = 1}^{n} f_{i - 1, j + w_k} \times \left(\frac{w_k}{j + w_k}\right) \]

你发现这个东西假假假, 怎么办, 虽然说改成状态压缩可以过掉 \(\textrm{20 pts}\)

重新设计, 考虑 \(\mathcal{O} (n)\) 计算在 \(t \in [1, n]\) 中删除 \(1\) 单位的概率
好吧真的不太会, 没有搞出什么性质, 把部分分打了跑路

\(n \leq 20\)

\(f_{i, \mathbb{S}}\) 表示第 \(i\) 秒时状态为 \(\mathbb{S}\) 的概率
考虑转移

\[\textrm{let } f_{0, \mathbb{U}} = 1\\ f_{i, \mathbb{S}} = \sum_{j \notin \mathbb{S}} f_{i - 1, \mathbb{S} \cup j} \times \left(\frac{w_j}{\sum w_{\mathbb{S} \cup j}}\right) \]

如何统计答案?
枚举 \(1\) 号节点被删除的时间 \(t\)

\[ans = \sum_{t = 1}^{n} \sum_{1 \in \mathbb{S}} f_{t - 1, \mathbb{S}} \times \left(\frac{w_1}{\sum w_{\mathbb{S}}}\right) \times t \]

总时间复杂度 \(\mathcal{O} (n 2^n)\) , 经典
\(w_{\mathbb{S}}\) 考虑预处理, 不然复杂度劣了

\(a_i = 1\)

这种情况怎么做?
不难发现每次删除都是等概率的

所以需要计算 \(1\) 单元存活到时间 \(i\) 的概率, 等效于在 \(n - 1\) 个单元中选择 \(i - 1\) 个杀掉的概率
不难发现就是

\[\begin{align*} ans &= \sum_{t = 2}^{n} \left[{n - 1 \choose t - 1} (t - 1)! \prod_{i = n - t + 2}^{n} \frac{1}{i}\right] \times \frac{1}{n - t + 1} \times t + \frac{1}{n} \\ &= \frac{1}{n} + \sum_{t = 2}^{n} \left[\frac{(n - 1)!}{(n - t)!} \frac{(n - t + 1)!}{n!}\right] \times \frac{1}{n - t + 1} \times t \\ &= \frac{1}{n} + \sum_{t = 2}^{n} \frac{n - t + 1}{n} \times \frac{1}{n - t + 1} \times t \\ &= \sum_{t = 1}^{n} \frac{t}{n} \end{align*}\]

感觉不太对, 但是先把 \(\textrm{20 pts}\) 打了可以验证这档分

实现

框架

按照上面实现即可, 但是出题人为什么不给模数, 无敌了

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 1e5 + 20; // 改改改
const int MAXS = (1 << 20) + 1; // 改改改

int n;
int w[MAXN];

/*状态压缩 dp*/
class subtask1 {
private:
    struct node { int first, second; };
    double f[2][MAXS]; int now = 0, nxt = 1;
    std::basic_string<int> size[MAXN];
    std::basic_string<node> st[MAXS];
    int sigma[MAXS];

    void init() {
        // for (int S = 0; S < (1 << n); S++) st[S].clear(), sigma[S] = 0;
        // for (int i = 0; i <= n; i++) size[i].clear();

        for (int S = 0; S < (1 << n); S++) {
            size[__builtin_popcount(S)].push_back(S);
            for (int i = 1; i <= n; i++) {
                if ((S >> (i - 1)) & 1) { sigma[S] += w[i]; continue; }
                st[S].push_back({(S | (1 << (i - 1))), i});
            }
        }
    }

public:
    void solve() {
        init();
        double ans = 0;
        memset(f, 0, sizeof f);
        f[now][(1 << n) - 1] = 1.0;
        ans += f[now][(1 << n) - 1] / sigma[(1 << n) - 1] * w[1];
        for (int i = 1; i <= n; i++) {
            std::swap(now, nxt);
            for (int S : size[n - i]) {
                for (node Spre : st[S])
                    f[now][S] += f[nxt][Spre.first] / sigma[Spre.first] * w[Spre.second];
                if (S & 1) ans += f[now][S] * (i + 1) / sigma[S] * w[1];
            }
        }
        printf("%.10f", ans);
    }
} sub1;

/*ai = 1*/
class subtask3 {
private:

public:
    void solve() {
        double ans = 0;
        for (int i = 1; i <= n; i++) ans += i * 1.0 / n;
        printf("%.10f", ans);
    }
} sub3;

signed main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++) scanf("%lld", &w[i]);

    if (n <= 20) sub1.solve();
    else sub3.solve();

    return 0;
}

\(\rm{T2}\)

时间分配有一点问题, 先看这个, 避免上面代码太难打影响心态
还好只有 \(3\)

思路

先考虑部分分, 因为时间的问题

考虑 \(a_i\) 确定时怎么做, 不难发现枚举 \(x\) 可以让 \(a_i\) 固定
不难发现转移

\(f_{i, j}\) 表示前 \(i\) 条信息分了 \(j\) 个段的最小花费
然后就是典中典

\[\begin{align*} f_{i, j} &= \min_{k = 0}^{i - 1} \max_{}^{}\{f_{k, j - 1}, \textrm{sum}_i - \textrm{sum}_k\} \end{align*}\]

\((a_i + x) \textrm{ mod } m\) 是不是有什么 \(\rm{trick}\) 啊, 但是我不到啊

可以乱搞出 \(\mathcal{O} (nm \log nm)\) , 差不多过掉 \(60\%\)

实现

框架

首先枚举 \(x\) , 计算 \(a_i\) , \(\textrm{sum}\) , 二分计算最优解, 不同之间取最小即可

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 1e3 + 20; // 改改改

int n, m, k;
int base[MAXN], w[MAXN];
int ans = 0x3f3f3f3f;

bool check(int x) {
    int sum = 0, tmp = 0;
    for (int i = 1; i <= n; i++) {
        if (w[i] > x) return false;
        sum += w[i]; if (sum > x) tmp++, sum = w[i];
    }
    return (tmp + 1) <= k;
}

int solve(int x) {
    /*处理当前 a*/ for (int i = 1; i <= n; i++) w[i] = ((base[i] + x) >= m ? base[i] + x - m : base[i] + x);
    int left = 0, right = n * m + 1, mid, res = 0x3f3f3f3f;
    while (left < right) {
        mid = left + (right - left) / 2;
        if (check(mid)) res = mid, right = mid;
        else left = mid + 1;
    }
    return res;
}

signed main()
{
    scanf("%lld %lld %lld", &n, &m, &k);
    for (int i = 1; i <= n; i++) scanf("%lld", &base[i]);

    for (int x = 0; x < m; x++) ans = std::min(ans, solve(x));
    printf("%lld", ans);

    return 0;
}

\(\rm{T3}\)

那我问你, 我这样子打得完代码就有鬼了\((\)谢谢 上帝/一个高尚无比美丽动人温文尔雅的名字 帮我把鬼打掉了, 请您帮我把挂分也打掉吧\()\)
虽然说这样不太好, 但是这场先这样, 节奏保持

思路

\(30\) 纯送

再来一档到达大众分, 但是我感觉以我的数学水平不太可能
好的没思路, 确实不太会, 先开始打, 最后过来干这个


好好好, 上帝啊, 让我再拼一档分吧
算了 上帝/一个高尚无比美丽动人温文尔雅的名字 你别帮了, 我不要在这个事上掉功德, 但是很感谢你帮我打完代码, 谢谢谢谢谢

一个合法的子集需要满足

  • 集内数互质
  • 集内出现的质数集合之积等于 \(n\)

\(n\) 分解质因数, 确定质数集合
不对, 会不会有其他质数集合之积也等于 \(n\) , 哦, 唯一分解定理证明了不会

所以我们对于集合中的每一个数, 可以拼给他一些质因数, 最多只有 \(20 \sim 30\) 个不同的质因数
那么显然可以进行一个状态压缩 \(\rm{dp}\) , 每次从质数集合之中拿出一个子集, 拼成一个数

遗憾, 没搞出来

posted @ 2025-02-20 11:51  Yorg  阅读(16)  评论(0)    收藏  举报