2.20 CW 模拟赛 赛时记录

前言#

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

看题#

给我干哪来了, 这是 Day 2 ?

T1#

最好和期望没啥关系

T2#

不太好想

T3#

多半是数学题了


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

T1#

思路#

题意

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

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

时间上限为 n , 可以接受

不难想到 dp
fi,j 表示第 i 秒时 w=j 的概率
什么你说 j 开不了, 超冰啊, 用 map 即可

考虑转移

let f0,w=1fi,j=k=1nfi1,j+wk×(wkj+wk)

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

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

n20#

fi,S 表示第 i 秒时状态为 S 的概率
考虑转移

let f0,U=1fi,S=jSfi1,Sj×(wjwSj)

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

ans=t=1n1Sft1,S×(w1wS)×t

总时间复杂度 O(n2n) , 经典
wS 考虑预处理, 不然复杂度劣了

ai=1#

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

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

ans=t=2n[(n1t1)(t1)!i=nt+2n1i]×1nt+1×t+1n=1n+t=2n[(n1)!(nt)!(nt+1)!n!]×1nt+1×t=1n+t=2nnt+1n×1nt+1×t=t=1ntn

感觉不太对, 但是先把 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;
}

T2#

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

思路#

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

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

fi,j 表示前 i 条信息分了 j 个段的最小花费
然后就是典中典

fi,j=mink=0i1max{fk,j1,sumisumk}

(ai+x) mod m 是不是有什么 trick 啊, 但是我不到啊

可以乱搞出 O(nmlognm) , 差不多过掉 60%

实现#

框架#

首先枚举 x , 计算 ai , 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;
}

T3#

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

思路#

30 纯送

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


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

一个合法的子集需要满足

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

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

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

遗憾, 没搞出来

posted @   Yorg  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示