第46屆ICPC亞洲區域賽(昆明)題解
C. Cup of Water
題意
每次會在\([0,a]\)之間隨機選一個數\(t\),往水桶裡裝\(t\)升水,問使得水桶裡的水大於等於\(1\)升的期望最少次數。其中\(0.05\leq a\leq 10^9\)。
方法一
利用多重積分可以證明,\(n\)維下,有\(n\)條長度為\(x\)的邊且它們互相垂直的物體(下稱單純形)的\(n\)維測度為
\[V_n=\frac{x^n}{n!} \]對於區域\(x_1+x_2+\dots+x_n\leq1\)與\(0\leq x_i\leq a\)的交集的測度可以使用容斥原理計算。
在\(x_1,x_2,\dots,x_n\geq 0\)的條件下,
任選\(0\)個變數,令其大於\(a\),其餘無額外約束,表示至少有\(0\)個變數大於\(a\),共有\(\binom{n}{0}\)種選法,所以要加上\(\binom{n}{1}\)個長度為\(1\)的單純形的測度
任選\(1\)個變數,令其大於\(a\),其餘無額外約束,表示至少有\(1\)個變數大於\(a\),共有\(\binom{n}{1}\)種選法,所以要減去\(\binom{n}{1}\)個長度為\(1-a\)的單純形的測度
任選\(2\)個變數,令其大於\(a\),其餘無額外約束,表示至少有\(2\)個變數大於\(a\),共有\(\binom{n}{2}\)種選法,所以要加上\(\binom{n}{2}\)
\(\dots\)
直到任選\(i\)個變數時,\(1-ia<0\),不存在有\(i\)個變數同時大於\(a\)的可能,計算結束,故式子如下
\[V_n=\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^{i}}{n!}\binom{n}{i}(1-ia)^n \]計隨機變數\(X\)為每次均勻隨機走\([0,a]\),達到大於等於\(1\)所需的最少次數,則有
\[\begin{aligned} P(X\leq k)&=1-\frac{1}{a^k}\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^{i}}{k!}\binom{k}{i}(1-ia)^k\\ &=1-\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^{i}}{k!}\binom{k}{i}\left(\frac{1}{a}-i\right)^k \end{aligned} \]由此可得
故期望為
\[\begin{aligned} E(X)&=\sum\limits_{k=1}^{\infty}kP(X=k)\\ &=\sum\limits_{k=1}^{\infty}k\left(\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^{i}}{(k-1)!}\binom{k-1}{i}\left(\frac{1}{a}-i\right)^{k-1}-\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^{i}}{k!}\binom{k}{i}\left(\frac{1}{a}-i\right)^k\right)\\ &=\sum\limits_{k=0}^{\infty}\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^{i}}{k!}\binom{k}{i}\left(\frac{1}{a}-i\right)^k\\ &=\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\sum\limits_{k=0}^{\infty}\frac{(-1)^{i}}{k!}\binom{k}{i}\left(\frac{1}{a}-i\right)^k\\ &=\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^i}{i!}\sum\limits_{k=0}^{\infty}\frac{1}{(k-i)!}\left(\frac{1}{a}-i\right)^k\\ &=\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^i}{i!}\left(\frac{1}{a}-i\right)^{i}\sum\limits_{k=0}^{\infty}\frac{1}{(k-i)!}\left(\frac{1}{a}-i\right)^{k-i}\\ &=\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^i}{i!}\left(\frac{1}{a}-i\right)^{i}\mathrm{e}^{\frac{1}{a}-i} \end{aligned} \]方法二
設\(f(x)\)為每次均勻隨機走\([0,a]\),達到大於等於\(x\)的期望次數,由全期望公式
\[f(x)=\int_{x-a}^x\left(\frac{f(t)+1}{a}\right)\mathrm{d}t=1+\int_{x-a}^x\frac{f(t)}{a}\mathrm{d}t \]兩側對\(x\)求導
\[af'(x)=f(x)-f(x-a) \]對於這個式子,有兩種處理辦法,一種是利用導數的定義進行近似計算,另一種是像方法一那樣,通過這個式子,推出最終結果的式子。
法一
進行近似計算,當\(\Delta x\to 0\)時,有
\[a\cdot \frac{f(x+\Delta x)-f(x)}{\Delta x}=f(x)-f(x-a)\\ f(x+\Delta x)=f(x)+\frac{\Delta x}{a}(f(x)-f(x-a))\\ \]令初值\(f(0)=1\),\(f(1)\)即為答案
由於題目是多組輸入,且步長\([0,a]\),大於等於\(1\)的期望步數,等價於,步長\([0,1]\),大於等於\(\frac{1}{a}\)的期望步數,故而把遞推式變成
\[f(x+\Delta x)=f(x)+\Delta x(f(x)-f(x-1))\\ \]令初值\(f(0)=1\),\(f(\frac{1}{a})\)即為答案,這樣在多組輸入之前就可以進行預處理。
法二
像法一一樣先轉化為步長為\([0,1]\)的問題,遞推式變成
\[f'(x)=f(x)-f(x-1) \]對\(x\)範圍進行分類討論
-
當\(0<x\leq 1\)時,\(f(x-1)=0\),
解得\(f(x)=C\mathrm{e}^x\),
當\(x\to 0^+\)時,\(f(x)\to1\),故\(C=1\),
\(f(x)=\mathrm{e}^x\)
-
當\(1<x\leq 2\)時,\(f(x-1)=\mathrm{e}^{x-1}\),
解得\(f(x)=C\mathrm{e}^x-x\mathrm{e}^{x-1}\),
由連續性,當\(x\to 1^+\)時,\(f(x)\to f(1)=\mathrm{e}\),故\(C=1+\frac{1}{e}\),
\(f(x)=\mathrm{e}^x-(x-1)\mathrm{e}^{x-1}\)
-
當\(2<x\leq 3\)時,\(f(x-1)=\mathrm{e}^{x-1}-(x-2)\mathrm{e}^{x-2}\)
解得\(f(x)=C\mathrm{e}^x-x\mathrm{e}^{x-1}+\frac{1}{2}(x-2)^2\mathrm{e}^{x-2}\),
由連續性,當\(x\to 2^+\)時,\(f(x)\to f(2)=\mathrm{e}^2-\mathrm{e}\),故\(C=1+\frac{1}{e}\),
\(f(x)=\mathrm{e}^x-(x-1)\mathrm{e}^{x-1}+\frac{1}{2}(x-2)^2\mathrm{e}^{x-2}\)
\(\dots\)
找規律,並數歸可得,當\(k<x\leq(k+1)\)時(\(k\)為非負整數)
\[f(x)=\sum\limits_{i=0}^k\frac{(-1)^i}{i!}(x-i)^i\mathrm{e}^{x-i} \]令\(x=\frac{1}{a}\),得
\[f(x)=\sum\limits_{i=0}^{\lfloor\frac{1}{a}\rfloor}\frac{(-1)^i}{i!}\left(\frac{1}{a}-i\right)^i\mathrm{e}^{\frac{1}{a}-i} \]程式碼
代式子
#include <cmath>
#include <cstdio>
void solve() {
double x;
scanf("%lf", &x);
x = 1 / x;
double ans = 0, fac = 1;
for (int i = 0; i < x; i++) {
if (i & 1)
ans -= fac * pow(x - i, i) * exp(x - i);
else
ans += fac * pow(x - i, i) * exp(x - i);
fac /= (i + 1);
}
printf("%.10lf\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--) solve();
return 0;
}
近似計算
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int maxn = 1e5;
const double dx = 1.0 / maxn;
double f[maxn * 20 + 100];
void solve() {
double x;
scanf("%lf", &x);
int t = 1 / x * maxn;
printf("%.10lf\n", f[t]);
}
int main() {
f[0] = 1;
for (int i = 0; i < 20 * maxn + 99; i++)
f[i + 1] = f[i] + dx * (f[i] - (i - maxn >= 0 ? f[i - maxn] : 0));
int T;
scanf("%d", &T);
while (T--)
solve();
return 0;
}