【题解】2019,4.20模拟赛 (白鼠)
\(Description:\)
给出一块巧克力,横着可以切 \(h\) 刀,竖着可以切 \(w\) 刀,横着切了 \(i\) ,竖着切了 \(j\) 可以得到 \((i+1)*(j+1)\) 块巧克力,每次切得代价是巧克力块数,求切 \(k\) 的期望代价,每次在剩余能切的位置等概率选一个切,答案对 \(10^9+7\) 取模。
\(Sample\) \(Input:\)
2 1 2
\(Sample\) \(Output:\)
666666677
$Solution: $
一开始考试的时候没啥子想法,瞎写了一个dp ,状态定的不错,只可惜忘记了他每次的概率也会变换,是要顺便在记一个概率的。。。
\(Sol_1:\)
正解有一种dp,跟我想法差不多:
记 \(f[i][j]\) 表示总共切了 \(i\) 刀,横着切了 \(j\) 刀的概率
再记一个 \(g[i][j]\) 表示总共切 $ i $ 刀,横着切了 \(j\) 刀的期望。
\(Sol_2:\)
直接暴力处理出两个数组,一个是总方案数,另一个记总代价。
最后两者直接除一除。
注意逆元和切得位置不同,切得顺序不同都算不同方案。
我只写了第二种方法,代码短,好理解。
#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
int h,w,k,ans,sum;
const int N=5000+5,p=1e9+7;
int f[N][N],g[N][N];
inline int power(int a,int b){
int ret=1;
while(b){
if(b&1) ret=(ret*a)%p;
a=(a*a)%p;
b>>=1;
}
return ret;
}
signed main(){
scanf("%lld%lld%lld",&h,&w,&k);
g[0][0]=1;
for(int i=1;i<=min(h,k);++i){
g[i][0]=g[i-1][0]*(h-i+1)%p;
f[i][0]=f[i-1][0]*(h-i+1)%p;
f[i][0]=(f[i][0]+(i+1)*g[i][0]%p)%p;
}
for(int j=1;j<=min(w,k);++j){
g[0][j]=g[0][j-1]*(w-j+1)%p;
f[0][j]=f[0][j-1]*(w-j+1)%p;
f[0][j]=(f[0][j]+(j+1)*g[0][j]%p)%p;
}
for(int i=1;i<=min(h,k);++i) for(int j=1;j<=min(w,k-i);++j){
g[i][j]=(g[i][j]+g[i-1][j]*(h-i+1)%p)%p;
g[i][j]=(g[i][j]+g[i][j-1]*(w-j+1)%p)%p;
f[i][j]=(i+1)*(j+1)%p*g[i][j]%p;
f[i][j]=(f[i][j]+f[i-1][j]*(h-i+1)%p)%p;
f[i][j]=(f[i][j]+f[i][j-1]*(w-j+1)%p)%p;
}
for(int i=0;i<=h;++i) if(k-i<=w)
ans=(ans+f[i][k-i])%p,sum=(sum+g[i][k-i])%p;
printf("%lld\n",ans*power(sum,p-2)%p);
return 0;
}