1. 程式人生 > 實用技巧 >P1291 [SHOI2002]百事世界盃之旅

P1291 [SHOI2002]百事世界盃之旅

題目描述

Link

你關上電視,心想:假設有 \(n\) 個不同的球星名字,每個名字出現的概率相同,平均需要買幾瓶飲料才能湊齊所有的名字呢?


期望dp 水題 (bushi)。但這輸出也未免太毒瘤了吧。

\(f[i]\) 表示當前當前有 \(i\) 個球星的名字的期望。

我們有 \(i\over n\) 的概率抽到的是之前取到的,這時候我們就還需要再抽 \(f[i]\) 次,所以期望就是 \({i\over n}\times (f[i] + 1)\)

還有 \({n-i}\over i\) 的概率抽到的是之前沒有取到的,這時候我們只需要再取 \(f[i-1]\) 次就可以,那麼期望就是 \({{n-i}\over i} \times ({f[i-1] + 1})\)

然後我們就得到了遞推式 \(f[i] = {i\over n}\times (f[i] + 1) + {{n-i}\over i} \times ({f[i-1] + 1})\)

化簡一下可以得到: \(f[i] = f[i-1] + {n\over{n-i}}\)

轉化一下形式變為 \(\displaystyle\sum_{i=1}^{n} {n\over{n-i}}\)

然後在求的的時候注意通分一下就行。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
int n,mu,zi,ans;
int gcd(int a,int b)
{
	if(b == 0) return a;
	else return gcd(b,a%b);
}
int wei(int x)
{
	int res = 0;
	while(x)
	{
		res++;
		x /= 10;
	}
	return res;
}
signed main()
{
	scanf("%lld",&n);
	mu = 1, zi = n;
	for(int i = 2; i <= n; i++)
	{
		zi = zi * i + n * mu;//通分
		mu = mu * i;
		int res = gcd(mu,zi);
		mu /= res;
		zi /= res;
	}
	int ans = zi / mu;//求帶數
	zi %= mu;
	if(zi == 0) printf("%lld\n",ans);
	else
	{
		for(int i = 1; i <= wei(ans); i++) printf(" ");
		printf("%lld\n",zi);
		printf("%lld",ans);
		for(int i = 1; i <= wei(mu); i++) printf("-");
		printf("\n");
		for(int i = 1; i <= wei(ans); i++) printf(" ");
		printf("%lld\n",mu);
	}
	return 0;
}