方格染色's 题解

纯粹的推公式题???(组合计数+概率期望)

题目链接

题目大致就是说给一个 n ∗ n n*n nn 大小的格子上填入 1   m 1~m 1 m 中的 n ∗ n n*n nn 个数( m > n ∗ n m>n*n m>nn),然后选择 k k k 个数涂黑(可能不在棋盘上),若有 r r r c c c 列被全部涂黑,那么就有 2 r + c 2^{r+c} 2r+c 的得分,问期望得分。

那么转化一下, 2 r + c 2^{r+c} 2r+c 其实就是全涂黑的行和列的子集数目,设涂黑的行和列的集合为 R R R C C C,那么可以看出最后答案就是 ∑ R , C P R , C \sum_{R,C} P_{R,C} R,CPR,C,那么 r = ∣ R ∣ r=|R| r=R c = ∣ C ∣ c=|C| c=C,那么可以得出 P R , C = ( k − x m − x ) ( k m ) P_{R,C}=\frac{(^{m-x}_{k-x})}{(^m_k)} PR,C=(km)(kxmx) x = n ∗ ( r + c ) − r − c x=n*(r+c)-r-c x=n(r+c)rc,即必须被涂黑的格子数),所以答案转化为了 ∑ r = 0 n ∑ c = 0 n ( r n ) ( c n ) ( k − x m − x ) ( k m ) \sum^n_{r=0}\sum^n_{c=0}(^n_r)(^n_c)\frac{(^{m-x}_{k-x})}{(^m_k)} r=0nc=0n(rn)(cn)(km)(kxmx)

你以为这样就结束了???并没有。

你会发现 m m m 过大,不可能直接用阶乘推出组合数。那么考虑下一步优化。

观察 ( r n ) (^n_r) (rn) ( c n ) (^n_c) (cn),发现它们的下标一直为 n n n,那么可以考虑相邻项推出递推式, ( x n ) ( x − 1 n ) = n ! x ! ( n − x ) ! ∗ ( x − 1 ) ! ( n − x + 1 ) ! n ! = n − x + 1 x \frac{(^n_x)}{(^n_{x-1})}=\frac{n!}{x!(n-x)!}*\frac{(x-1)!(n-x+1)!}{n!}=\frac{n-x+1}x (x1n)(xn)=x!(nx)!n!n!(x1)!(nx+1)!=xnx+1,即 ( x n ) = ( x − 1 n ) ∗ n − x + 1 x (^n_x)=(^n_{x-1})*\frac{n-x+1}x (xn)=(x1n)xnx+1,用 q [ x ] q[x] q[x] 来表示 ( x n ) (^n_x) (xn),边界 q [ 0 ] = 1 q[0]=1 q[0]=1

然后来考虑 ( k − x m − x ) ( k m ) \frac{(^{m-x}_{k-x})}{(^m_k)} (km)(kxmx),显然,它们的上下标差值不会变,它的大小只和 x x x 的大小有关,那么可以设 f ( x ) = ( k − x m − x ) ( k m ) f(x)=\frac{(^{m-x}_{k-x})}{(^m_k)} f(x)=(km)(kxmx),于是有 f ( x ) = ( m − x ) ! ( k − x ) ! ( m − k ) ! ∗ k ! ( m − k ) ! m ! = ( m − x ) ! k ! ( k − x ) ! m ! f(x)=\frac{(m-x)!}{(k-x)!(m-k)!}*\frac{k!(m-k)!}{m!}=\frac{(m-x)!k!}{(k-x)!m!} f(x)=(kx)!(mk)!(mx)!m!k!(mk)!=(kx)!m!(mx)!k!,那么 f ( x − 1 ) = ( m − x + 1 ) ! k ! ( k − x + 1 ) ! m ! f(x-1)=\frac{(m-x+1)!k!}{(k-x+1)!m!} f(x1)=(kx+1)!m!(mx+1)!k!。所以 f ( x ) f ( x − 1 ) = k − x + 1 m − x + 1 \frac{f(x)}{f(x-1)}=\frac{k-x+1}{m-x+1} f(x1)f(x)=mx+1kx+1,即 f ( x ) = f ( x − 1 ) ∗ k − x + 1 m − x + 1 f(x)=f(x-1)*\frac{k-x+1}{m-x+1} f(x)=f(x1)mx+1kx+1,用 c [ x ] c[x] c[x] 来来表示 f ( x ) f(x) f(x),边界 c [ 0 ] = 1 c[0]=1 c[0]=1

然后就可以得出答案了。

顺带说一句,代码一旦大于莫个数就会炸(实验证明这个数为999999999999999967336168804116691273849533185806555472917961779471295845921727862608739868455469056,我也没办法解决处于它和1e99之间的数,这部分的数只有保留整数才能输出,而且说实话我造不出来),看书上的代码明显不可能输出的,而且我特判输出那部分删掉交一遍还是过了,自己造数据的时候随机输出的基本都不会很大的(所以刻意造数据了嘻嘻嘻)。还有,那个spj我不会啊。。。(如果到时间还写不出来那就除了1e99的数全部输出6位小数啦)

OK就这样(感觉自己好难。。。)

上code:

#include
using namespace std;
const int N=300+2,M=1e5+10;
int n,m,k,ans;
double c[N],q[M],sum;
void init(){
	scanf("%d%d%d",&n,&m,&k);
	c[0]=q[0]=1.0;
	for(int i=0;++i<=n;c[i]=c[i-1]*(n-i+1)/(1.0*i));
	for(int i=0;++i<=k;q[i]=q[i-1]*(k-i+1)/(1.0*(m-i+1)));
}
void work(){
	for(int i=-1;++i<=n;) for(int j=-1;++j<=n;
		ans=n*(i+j)-i*j,ans>k?:sum+=c[i]*c[j]*q[ans]);
}
void prin(){
	if(sum>1e99){
		cout<<1e99;
		return ;
	}
	printf("%.6lf",sum); 
}
int main(){
	init();
	work();
	prin();
	return 0;
}

感谢各位dalao聆听。

呃,更新一下,为了方便起见,oj上传的题目为超过1e99的输出-1,自行注意啦,上面的我就不改了。(CaO?貌似没改???)

你可能感兴趣的:(题解)