[SHOI2002]百事世界杯之旅 \color{green}{\texttt{[SHOI2002]百事世界杯之旅}} [SHOI2002]百事世界杯之旅
[Problem] \color{blue}{\texttt{[Problem]}} [Problem]
有 n n n 个等可能的奖品,买一瓶饮料就可以得到一个奖品,求期望下买多少瓶饮料可以获得所有的奖品。
以假分数的形式输出答案。
[Solution] \color{blue}{\texttt{[Solution]}} [Solution]
记 f i f_i fi 表示期望下买多少瓶饮料可以获得 i i i 中奖品。
考虑如何推导出 f i + 1 f_{i+1} fi+1。
- 从 f i f_{i} fi 推导来:即得了一瓶现在还没有的奖品,有 n − i n \dfrac{n-i}{n} nn−i 的可能性,所以期望为 n − i n × ( f i + 1 ) \dfrac{n-i}{n} \times \left (f_{i}+1 \right ) nn−i×(fi+1)。
- 从 f i + 1 f_{i+1} fi+1 推导来:即得到了已有的奖品,有 i + 1 n \dfrac{i+1}{n} ni+1 的可能性,期望为 i + 1 n × ( f i + 1 + 1 ) \dfrac{i+1}{n} \times \left (f_{i+1} +1\right) ni+1×(fi+1+1)。
所以, f i + 1 = n − i n × ( f i + 1 ) + i + 1 n × ( f i + 1 + 1 ) f_{i+1}=\dfrac{n-i}{n} \times \left ( f_{i} +1 \right) + \dfrac{i+1}{n} \times \left ( f_{i+1} +1 \right ) fi+1=nn−i×(fi+1)+ni+1×(fi+1+1)。
这个式子看似非常显然,但是有个问题:为什么概率的总和(即可能性的总和)会对于 1 1 1 呢?
从 f i f_i fi 推导来的是没有错的,只能是从 f i + 1 f_{i+1} fi+1 推导来的错了。但是,错在哪里了呢?
回想一下生活中你买饮料时的经历,当我们凑齐了所有的奖品之后,我们就不会再买饮料了 (除非你很想喝或想再中一次)。这里也一样。
因为我们考虑 f i + 1 f_{i+1} fi+1,所以我们把这 ( i + 1 ) (i+1) (i+1) 种奖品就看作所有的奖品。我们把思维逆过来——即从 ( i + 1 ) (i+1) (i+1) 种奖品中放弃一种奖品。如果我们的统计是对的,那么我们把过程逆过来也应该要得到相同的结论。
我们不断地放走一个奖品,那么总会有一个奖品满足:放弃它之后我们就只有 i i i 种奖品了,我们记它为第 ( i + 1 ) (i+1) (i+1) 种奖品。
于是我们在考虑加入第 ( i + 1 ) (i+1) (i+1) 种奖品的时候,在得到它之前其实我们只有 i i i 种奖品,所以原来的分类方法会产生重复,重复就在第 ( i + 1 ) (i+1) (i+1) 种奖品这里。
既然产生了重复,那么我们把第 ( i + 1 ) (i+1) (i+1) 种奖品算哪里呢?算在 f i f_{i} fi 中,为什么呢?
这么来想:还是那句话,已经得到了 ( i + 1 ) (i+1) (i+1) 种奖品时我们不会再拿奖品,那么在我们拿到了 ( i + 1 ) (i+1) (i+1) 中奖品后,我们再拿这里的奖品就是为了获得第 ( i + 2 ) (i+2) (i+2) 种奖品了(比如你有 4 4 4 种奖品了,那么你再拿这 4 4 4 种奖品中的一个其实是为了拿第 5 5 5 种奖品对吧)。所以我们算在 f i f_{i} fi 中。
于是 f i + 1 = n − i n × ( f i + 1 ) + i n × ( f i + 1 + 1 ) f_{i+1}=\dfrac{n-i}{n} \times \left (f_{i}+1 \right ) + \dfrac{i}{n} \times \left ( f_{i+1}+1 \right ) fi+1=nn−i×(fi+1)+ni×(fi+1+1)。
稍微地移一下项,可以得到: f i + 1 = f i + n n − i f_{i+1}=f_{i} + \dfrac{n}{n-i} fi+1=fi+n−in。
再加上一点处理分数的知识,就可以 O ( n × log n ) O(n \times \log n) O(n×logn) 解决了本题(为什么有个 log \log log,因为要求 gcd \gcd gcd)。
[code] \color{blue}{\texttt{[code]}} [code]
typedef long long ll;
inline ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
inline int digit_num(ll t){
register int ret=0;
while (t){ret++;t/=10;}
return ret;
}
ll t[35],f[35],g[35];int n;
int main(){
cin>>n;f[1]=1;g[1]=1;
for(int i=1;i<n;i++){
g[i+1]=g[i]*(n-i);
f[i+1]=f[i]*(n-i)+g[i]*n;
int tmp=gcd(g[i+1],f[i+1]);
f[i+1]/=tmp;g[i+1]/=tmp;
t[i+1]=t[i]+f[i+1]/g[i+1];
f[i+1]%=g[i+1];
}
if (f[n]==0) printf("%lld",t[n]);
else{
int num=digit_num(t[n]);
int sum=digit_num(g[n]);
for(int i=1;i<=num;i++)
printf(" ");
printf("%lld\n",f[n]);
printf("%lld",t[n]);
for(int i=1;i<=sum;i++)
printf("-");
printf("\n");//注意换行
for(int i=1;i<=num;i++)
printf(" ");
printf("%lld",g[n]);
}
}