题目大意
题目总结
Bob 有 n 瓶酒精饮料,标号为 1 到 n。已知第 i 瓶饮料包含 vi 单位的液体体积,其中有 ai 单位是纯酒精(0≤ai≤vi)。因此,第 i 瓶饮料的酒精浓度为 ai/vi。每瓶饮料是完全均匀的,从中取出任意体积的液体,其酒精浓度均保持不变。
Bob 可以从这些瓶子中选择任意组合,并从每瓶中取出任意体积(不必须为整数)的液体进行混合。
现在提出一个挑战:
令 V=∑i=1nvi 为所有瓶子中液体的总体积。
在 [0,V] 区间内均匀随机生成一个实数 s(目标总体积);
在 [0,1] 区间内均匀随机生成一个实数 f(目标酒精浓度)。
请计算:Bob 能够成功调配出总体积恰好为 s、且酒精浓度恰好为 f 的饮品的概率是多少?(输出结果与标准答案的绝对或相对误差需不超过 10−8)。
输入格式
第一行包含一个整数 n(2≤n≤2×105)。
第二行包含 n 个整数,依次为 v1,v2,…,vn(1≤vi≤109)。
第三行包含 n 个整数,依次为 a1,a2,…,an(0≤ai≤vi)。
样例输入
1 2 3
| 3 350 750 330 140 131 16
|
样例输出
样例解释
在这个样例中,所有液体的总体积 V=350+750+330=1430。
我们可以考虑两种具体的情况:
第一种情况:假设生成的 s=500 且 f=133。此时 Bob 是可以成功调配的。例如,他可以从第 1 瓶中取出 37797750 单位的液体,从第 3 瓶中取出 37790750 单位的液体。
调配出的总体积为 37797750+37790750=377188500=500。
调配出的酒精浓度为:
50037797750×350140+37790750×33016=133
第二种情况:假设生成的 s=814 且 f=0.1234567。此时 Bob 是无法完成调配的,因为利用他现有的饮料集合无法混合出该体积和浓度的饮品。
综合考虑所有可能在给定范围内均匀生成的 s 和 f 的数值对 (s,f),Bob 能够成功调配的概率约为 0.19356182654786474591。
思路讲解
把概率,转化为可行域面积占总面积的比重,这一点对两参数随意取的概率问题特别有用。

具体而言,我们的这个分段函数应该长这样:

其不定积分是长这样的,带进去值,算一下定积分即可。

AC代码
AC
https://qoj.ac/submission/2106344
AC
https://codeforces.com/gym/106262/submission/365722146
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
#include <bits/stdc++.h> #define all(vec) vec.begin(),vec.end() #define lson(o) (o<<1) #define rson(o) (o<<1|1) #define SZ(a) ((long long) a.size()) #define debug(var) cerr << #var <<" = ["<<var<<"]"<<"\n"; #define debug1d(a) \ cerr << #a << " = ["; \ for (int i = 0; i < (int)(a).size(); i++) \ cerr << (i ? ", " : "") << a[i]; \ cerr << "]\n"; #define debug2d(a) \ cerr << #a << " = [\n"; \ for (int i = 0; i < (int)(a).size(); i++) \ { \ cerr << " ["; \ for (int j = 0; j < (int)(a[i]).size(); j++) \ cerr << (j ? ", " : "") << a[i][j]; \ cerr << "]\n"; \ } \ cerr << "]\n"; #define cend cerr<<"\n-----------\n" #define fsp(x) fixed<<setprecision(x)
using namespace std;
using ll = long long; using ull = unsigned long long; using DB = long double; using i128 = __int128; using CD = complex<double>;
static constexpr ll MAXN = (ll)1e6+10, INF = (1ll<<61)-1; static constexpr ll mod = 998244353; static constexpr double eps = 1e-8; const double pi = acos(-1.0);
ll lT,testcase;
struct V_A { ll v,a; DB c; }; DB calVal(ll A,ll V,DB c,ll x) { if (x<=0) { return 0; } DB res=A*log(x)+c*x-c*V*log(x); return res; } void Solve() { ll N; cin >> N; vector<V_A> wine_inc(N+2); ll sumv=0; for (int i=1;i<=N;++i) { cin>>wine_inc[i].v; sumv+=wine_inc[i].v; } for (int i=1;i<=N;++i) { cin>>wine_inc[i].a; wine_inc[i].c=DB(wine_inc[i].a)/wine_inc[i].v; } vector<V_A> wine_dec=wine_inc; sort(wine_inc.begin()+1,wine_inc.begin()+N+1,[](const V_A &a,const V_A &b) { return a.c<b.c; }); sort(wine_dec.begin()+1,wine_dec.begin()+N+1,[](const V_A &a,const V_A &b) { return a.c>b.c; }); DB mn_area=0; do { ll upA=0; ll upV=0; for (int i=1;i<=N;++i) { ll curV=upV+wine_inc[i].v; mn_area+=calVal(upA,upV,wine_inc[i].c,curV)-calVal(upA,upV,wine_inc[i].c,upV); upA=upA+wine_inc[i].a; upV=upV+wine_inc[i].v; } }while (false); DB mx_area=0; do { ll upA=0; ll upV=0; for (int i=1;i<=N;++i) { ll curV=upV+wine_dec[i].v; mx_area+=calVal(upA,upV,wine_dec[i].c,curV)-calVal(upA,upV,wine_dec[i].c,upV); upA=upA+wine_dec[i].a; upV=upV+wine_dec[i].v; } }while (false); DB area=mx_area-mn_area; DB ans=area/sumv; cout<<fsp(15)<<ans<<"\n"; }
signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); #ifdef LOCAL cout.setf(ios::unitbuf); #endif
Solve(); return 0; }
|
心路历程(WA,TLE,MLE……)
注意,分段函数不是线性的,而是一个分段反比例函数。

不要一会用 x,一会用 v,但是代表同一个意思。
