CF229E 题解

前言

基本是官方题解的思路。

分析

先考虑不用纠结的情况。假设第 \(n+1\) 大价值的物品的价值小于第 \(n\) 大的。

此时物品名称集合可以确定。

对于每类名称,恰好拿到的概率为 \(1/\dbinom{k_i}{a_i}\)\(a_i\) 为选择的物品数量,\(k_i\) 为总数。

对于不同类名称,鱼的选择间独立。将这些概率乘起来就行了。

再考虑要纠结的情况。假设和第 \(n\) 大价值的物品相同价值的是第 \(l\)\(r\) 个,\(l \le n \lt r\)

一种做法是,暴力求出所有 \(\dbinom{r-l+1}{n-l+1}\) 种情况,对于它们的概率取平均值。

对于所有种情况的概率之和,可以 dp 求出。具体地,设 \(dp(i,j)\)\([l, l+i)\) 中的物品,选了 \(j\) 个,所有合法情况的概率之和,那么 \(dp(r-l+1, n-l+1)\) 即为答案。

对于一个状态 \((i,j)\),可以从 \((i-1, j)\)(不选)和 \((i-1, j-1)\)(选)转移来。

考虑一个名称。当我想从选 \(a_i\) 个变为选 \(a_i+1\) 个时,概率变化为 \({p_i \dbinom{k_i}{a_i}}/{\dbinom{k_i}{a_i+1}} = \dfrac{p_i(a_i+1)}{k_i-a_i}\)\(p_i\) 是原先的概率。

最后别忘了除以情况数 \(\dbinom{r-l+1}{n-l+1}\)

#include <bits/extc++.h>
#define UP(i,s,e) for(auto i=s; i<e; ++i)
#define IT(i,s,e) for(auto i=s; i!=e; ++i)
#define pbds __gnu_pbds
using std::cin; using std::cout;
namespace m{ // }{{{
template<typename T> using Tree = pbds::tree<T, pbds::null_type, std::less<>, pbds::rb_tree_tag, pbds::tree_order_statistics_node_update>;
constexpr int N = 1e3, M = 1e3; int in, im; Tree<std::pair<int, int>> gift_rk;
std::vector<int> gifts[M];
int choose[M];
double dp[N+10][N+10];
double inv_binom(int x, int y){
    double ans = 1;
    UP(i, 0, y){
        ans *= (y-i)*1.0/(x-i);
    }
    return ans;
}
void work(){
    cin >> in >> im;
    UP(i, 0, im){
        int ik; cin >> ik;
        UP(j, 0, ik){
            int val; cin >> val;
            gift_rk.insert({-val, i});
            gifts[i].push_back(val);
        }
    }
    auto beg = gift_rk.lower_bound({gift_rk.find_by_order(in)->first, -1});
    auto end = gift_rk.lower_bound({gift_rk.find_by_order(in)->first, 114514});
    int len = std::distance(beg, end);
    IT(i, gift_rk.begin(), beg){
        choose[i->second]++;
        in--;
    }
    double &ans = dp[0][0]; ans = 1;
    UP(i, 0, im){
        ans *= inv_binom(gifts[i].size(), choose[i]);
    }
    auto it = beg;
    UP(i, 1, len+1){
        UP(j, 0, in+1){
            dp[i][j] = dp[i-1][j];
            if(j>0){
                dp[i][j] += dp[i-1][j-1] 
                * (choose[it->second]+1) 
                / (gifts[it->second].size() - choose[it->second]);
            }
        }
        it++;
    }
    printf("%.10lf", dp[len][in] * inv_binom(len, in));
}
} // {}}}
int main(){m::work(); return 0;}
posted @ 2023-10-23 06:22  383494  阅读(15)  评论(0)    收藏  举报