zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【Luogu1291】百事世界杯之旅(动态规划,数学期望)

规划 动态 之旅 数学 期望 世界杯
2023-09-11 14:14:41 时间

【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;
}