【Luogu1291】百事世界杯之旅(动态规划,数学期望)
题面
题解
设\\(f[i]\\)表示已经集齐了\\(i\\)个名字的期望
现在有两种方法:
先说我自己的:
\\[f[i]=f[i-1]+1+(1-p)(1*p^1+2*p^2+....)
\\]
其中\\(p=\\frac{i-1}{n}\\)
为什么,很简单
首先要多收集一个,期望\\(+1\\)是显然的
但是还可能一直买到了已经有的名字中的一个
有\\(p\\)的概率多买一个
\\(p^2\\)的概率多买两个
这样无穷的算下去
然后对于后面那个式子
做两次错位相减(其实就是一个无穷级数)
推出
\\[f[i]=f[i-1]+1+\\frac{i-1}{n-(i-1)}
\\]
然后递推就行了
第二种方法:
\\(fdf\\)的方法(感觉这种方法也很强呀)
设\\(f[i]\\)表示已经收集了\\(i\\)个
收集到\\(n\\)个需要的期望
\\[f[i]=\\frac{i}{n}(f[i]+1)+\\frac{n-i}{n}(f[i+1]+1)
\\]
\\[\\frac{n-i}{n}f[i]=\\frac{n-i}{n}f[i+1]+1
\\]
\\[f[i]=f[i+1]+\\frac{n}{n-i}
\\]
初值:\\(f[n]=0\\)
倒着算即可
贴上我自己的代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<\'0\'||ch>\'9\')&&ch!=\'-\')ch=getchar();
if(ch==\'-\')t=-1,ch=getchar();
while(ch<=\'9\'&&ch>=\'0\')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n;
int ws(ll x)
{
int ret=0;
while(x)++ret,x/=10;
return ret;
}
struct Num
{
ll a,b;
void easy()
{
ll d=__gcd(a,b);
a/=d;b/=d;
}
void output()
{
easy();
if(b==1){printf("%lld\\n",a);return;}
ll k=a/b;a-=k*b;
int blk=ws(k),ss=max(ws(a),ws(b));
for(int i=1;i<=blk;++i)putchar(\' \');
printf("%lld\\n",a);
printf("%lld",k);
for(int i=1;i<=ss;++i)putchar(\'-\');putchar(\'\\n\');
for(int i=1;i<=blk;++i)putchar(\' \');
printf("%lld\\n",b);
}
}f[50];
Num operator+(Num a,Num b)
{
ll d=a.b/__gcd(a.b,b.b)*b.b;
return (Num){a.a*(d/a.b)+b.a*(d/b.b),d};
}
Num operator*(Num a,Num b)
{
Num c=(Num){a.a*b.a,a.b*b.b};
c.easy();
return c;
}
int main()
{
n=read();
f[0]=(Num){0,1};
for(int i=1;i<=n;++i)
{
f[i]=f[i-1]+(Num){1,1};
f[i]=f[i]+(Num){i-1,n-i+1};
}
f[n].output();
return 0;
}