题解:[SHOI2002]百事世界杯之旅

Sol

题目大意

n n 个不同的物品,得到其中一个物品的概率相同,问若要得到所有物品平均需要买几次

题解

貌似之前楼上的题解的公式虽然推的大概是对的,但是讲解是错的而且就连i 的范围也是错的(真是尴尬)
我考虑的是以 f[i] f [ i ] 表示已经得到其中 i i 个物品,还需得到ni然后所期望的平均次数,最后答案在 f[0] f [ 0 ]
那么,你可能会列出如下两种转移方程式
一:由 f[i1] f [ i − 1 ] 推到 f[i] f [ i ]

f[i]=inf[i]+ni+1nf[i1]+1 f [ i ] = i n ∗ f [ i ] + n − i + 1 n ∗ f [ i − 1 ] + 1

二:由 f[i+1] f [ i + 1 ] 推到 f[i] f [ i ]
f[i]=inf[i]+ninf[i+1]+1 f [ i ] = i n ∗ f [ i ] + n − i n ∗ f [ i + 1 ] + 1

所以大家应该发现你考虑 正着推倒着推是不一样的,那么
Q:哪种方式才是对的呢
A:显然方式二啊
Q:qwq,大佬说说为什么吧
A:首先方案二中的 +1才有道理,其本质是 in i n + nin n − i n =1
这才是正确的期望=贡献*概率,然而方案一中的 +1 + 1 毫无道理,你究竟是在计算 f[i] f [ i ] 还是 f[i1] f [ i − 1 ] 时加的呢?
Q:好像还是不太懂啊
A:好吧,在终止状态唯一确定时,你会发现倒着推才能表示出全部方案,即并不是我一旦收集到 n n 个瓶子就停下,而是还可能可有由它自己推到或者经过一番环之后再推到
Q:好啊(我最喜欢记结论了),但是环是什么意思?大佬
A:你没发现具有递推关系的期望dp就是一个图吗?边有边权,上个状态与本步状态之前存在连边,可能一些特殊的图还有环等其他的性质
Q:哦!好像yy出来了,谢谢大佬
A:#@(U)#@E

由上文发现,

f[i]=inf[i]+ninf[i+1]+1

经过一波合并同类项并约分后
f[i]=nni+f[i+1] f [ i ] = n n − i + f [ i + 1 ]

再将i=i-1代入
f[i1]=nni+1+f[i] f [ i − 1 ] = n n − i + 1 + f [ i ]

由于是倒着推的,现在已知 f[i] f [ i ] ,推的 f[i1] f [ i − 1 ] ,那么答案不就是
i=1n1ni+1 ∑ i = 1 n 1 n − i + 1

f[n]=0 f [ n ] = 0
这就是答案,真简单

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long 
inline int read()
{
    char ch='!';int z=1,num=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')z=-1,ch=getchar();
    while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
    return z*num;
}
ll n;
ll gcd(ll a,ll b)
{
    if(!b)return a;
    else return gcd(b,a%b);
}
ll js(ll x)
{
    ll cnt=0;
    while(x)
    {
        x/=10;
        cnt++;
    }
    return cnt;
}
int main()
{
    n=read();
    if(n==1){puts("1");return 0;}
    if(n==2){puts("3");return 0;}
    ll f1=3,f2=2;
    for(int i=3;i<=n;++i)
    {
        ll c=gcd(f2,i);
        ll lcm=f2*i/c;
        ll d1=lcm/f2,d2=lcm/i;
        f1=f1*d1+d2;
        c=gcd(f1,lcm);
        f1/=c;f2=lcm/c;
    }
    f1*=n;
    ll c=gcd(f1,f2);
    f1/=c;f2/=c;
    ll k=f1/f2;
    ll a1=js(k);
    if(!f1)
    {
        cout<<k<<endl;
    }
    else
    {
        ll a2=js(f2);
        for(int i=1;i<=a1;++i)
            cout<<' ';
        cout<<f1%f2<<endl;
        if(k>=1)cout<<k;
        for(int i=1;i<=a2;++i)
            cout<<'-';
        cout<<endl;
        for(int i=1;i<=a1;++i)
            cout<<' ';
        cout<<f2<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值