1. 程式人生 > >ACM Qingdao Onsite 2016 C

ACM Qingdao Onsite 2016 C

思路來源

題意

你有一根餅乾棒,給定初始L和長度d,

若L≤d,就不啃了,期望次數為0。

否則,隨機選擇L上一個位置,把餅乾棒啃成左右兩半,

把左一半吃掉,若右一半長度大於d就重複該操作,直至啃完之後的右段小於d。

問:啃的期望次數。

題解

本來可以考慮概率dp,

若有一個最小步長1可以限制的話,概率dp就可以解決了。

設i可以向i-1,i-2,…d,…,1轉移(由於一定啃成兩半,故不能向i和0轉移)

記dp[i]為長度為i的期望次數,

則i的期望為dp[i]=1/(i-1)*(dp[i-1]+...+dp[d+1]+0*d)+1

這裡,由於五位小數,所以將小數*1e5離散化為整數,概率dp會精度不夠

所以,我們考慮連續的情形,幾何概型,

這就變成了一道微分方程的題目QAQ

記f[x]為長度為x的期望次數,可取小數,

f[x]=(\int_{d}^{x}f[t]dt)/x+1 ①

x*(f[x]-1)=\int_{d}^{x}f[t]dt

求導,有f[x]+f'[x]*x-1=f[x]

f'[x]=1/x

f[x]=lnx+C,x>d;

①式中,x趨於d+時,f[x]趨於1,

f[d]=lnd+C=1C=1-lnd

綜上有,

f[x]=0,x<=d

f[x]=lnx+1-lnd,x>d;

程式碼

#include <iostream>
#include <algorithm> 
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const double INF=0x3f3f3f3f;
const int maxn=1e5+10; 
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int> 
#define si set<int>
#define pii pair<double,int> 
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a)) 
using namespace std;
int main()
{ 
    int n;
    sci(n);
    rep(i,0,n-1)
    {
    	double d,l;
    	sclf(l),sclf(d);
    	if(l<=d+eps)puts("0.000000"); 
    	else printf("%.6lf\n",log(l)-log(d)+1);
    }
	return 0;
}