【题解】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;
}
posted @ 2019-04-21 14:00  章鱼那个哥  阅读(111)  评论(0编辑  收藏  举报