【區間DP+期望】Gym101623E English Restaurant
阿新 • • 發佈:2019-01-05
【題目】
原題地址
一個餐廳有
張桌子,第
張座位數為
。先後有
群人來吃飯,人數為
中隨機一個數。他們會找一張恰好能坐下的桌子坐下(找不到就離開)。求最終期望有多少個人吃飯。
【解題思路】
首先我們可以先新增
個座位
的桌子表示離開餐館,然後按座位數排序。
考慮用期望的定義來求答案,同時 出總和和方案數。
注意到最後佔據的一定是若干個區間,每個區間可以獨立計算。
我們設 表示只有 的桌子被佔據的人數總和以及方案數,列舉最後一張坐的桌子是什麼來進行轉移,這裡有一個組合數 表示前 張桌子的排列種類數。
然後設 表示 這些人佔據了 這些桌子的總和以及方案數,利用字首和優化轉移。
(或者我們可以設接下來設 表示前 張桌子佔了 張的期望。列舉 表示 都佔了, 不佔,當 表示 不佔,這裡有一個組合數 表示前 張桌子順序。)
複雜度
【參考程式碼】
#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef double db;
typedef long long ll;
typedef pair<db,db> pdd;
const int N=205;
int n,g,t,c[N];
db C[N][N];
pdd w[N][N],f[N][N],s[N][N];
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
pdd operator +(const pdd&a,const pdd&b){return mkp(a.fi*b.se+a.se*b.fi,a.se*b.se);}
pdd operator *(const pdd&a,const pdd&b){return mkp(a.fi+b.fi,a.se+b.se);}
void initC()
{
for(int i=0;i<N;++i)
{
C[i][i]=C[i][0]=1;
for(int j=1;j<i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
}
}
pdd calc(int l,int r)
{
int x=min(g,l>0?c[l-1]:0),y=min(g,c[r]);
return mkp(c[r]<N?(y*(y+1)-x*(x+1))/2:0,y-x);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("E.in","r",stdin);
freopen("E.out","w",stdout);
#endif
initC();n=read();g=read();t=read();
for(int i=0;i<n;++i) c[i]=read();
for(int i=0;i<t;++i) c[n++]=N;
sort(c,c+n);
for(int i=0;i<n;++i)
{
w[i][i]=calc(i,i);
for(int j=i-1;~j;--j) for(int k=j;k<=i;++k)
{
pdd tmp=mkp(0,C[i-j][k-j])+calc(j,k);
if(j<k) tmp=tmp+w[j][k-1];
if(i>k) tmp=tmp+w[k+1][i];
w[j][i]=w[j][i]*tmp;
}
}
for(int i=t-1;~i;--i) for(int j=n-1;~j;--j) f[i][j]=w[j][j+t-i-1];
for(int i=t-1;~i;--i) for(int j=n-1;~j;--j)
{
for(int k=i+1;k<t;++k)
f[i][j]=f[i][j]*(mkp(0,C[t-i][t-k])+w[j][j+k-i-1]+s[k][j+k-i+1]);
s[i][j]=s[i][j+1]*f[i][j];
}
pdd ans=mkp(0,0);
for(int i=0;i<n;++i) ans=ans*f[0][i];
printf("%.10lf\n",ans.fi/ans.se);
return 0;
}