ABC297F Minimum Bounding Box 2 题解

题意简述

定义 \((a,b)\) 表示网格中从左往右数第 \(a\) 个,从上往下数第 \(b\) 个方格。你需要在网格 \(V=\{(x,y)|x\in[1,H],y\in[1,W],x,y\in\mathbb{N}\}\)等概率随机选择 \(K\) 个互不相同的方格。求这 \(K\) 个点的最小覆盖矩形 \(R\) 覆盖方格的个数 \(S_R\) 的期望。这里 \(R\) 满足边都平行于网格的基准线。

解题思路

我们发现若要找出 \(R\),实在是有些麻烦。一个很好的思路是,因为每种选方格方式的概率都是一样的,于是把期望表示成:

\[E(S_R)=\frac{\text{(每种选方格情况下矩形覆盖方格个数的和)}}{\text{(选方格的情况总数)}} \]

但可以发现这也并不是特别好求。不妨思考:如何把抽象的、不确定位置的矩形,转化成确切的、已知的信息?我们发现:

\[{\text{(每种选方格情况下矩形覆盖方格个数的和)}}=\sum_{P\in V}{\text{所有情况下覆盖方格 } P \text{ 的矩阵个数}} \]

也就是说,我们只需要做到求单个方格被覆盖的次数,就可以求解全局的答案。

但是令人不爽的是,就算具体到如此程度,也不是很好求。考虑正难则反,求所有情况下不覆盖方格 \(P(a,b)\) 的矩形个数。这个就很好求了。设选出的方格的集合是 \(C(C\subseteq V,|C|=K)\),则应当满足

\[\begin{aligned} &\forall (x,y)\in C,x<a,\\ &\forall (x,y)\in C,x>a,\\ &\forall (x,y)\in C,y<b,\\ &\forall (x,y)\in C,y>b.\\ &\end{aligned} \]

中的至少一条

但是我们发现可能会重复统计完全在左上、左下、右上、右下的情况。容斥一下就可以了。具体实现可以见下面的代码。

代码实现

#include<cstdio>
#include<iostream>
#include<vector>

//此处省略 10000 字快读。

#define int long long
#define R myio::read_int() 
//也就是说,后面的 R 表示读入一个整数。

const int S=1e6,P=998244353;

int fac[S+6],inv[S+6];
int Qpow(int x,int y) {
	int ret=1;
	for(;y>0;y>>=1) {
		if(y&1) (ret*=x)%=P;
		(x*=x)%=P;
	} return ret;
}
void pret() { //预处理阶乘与阶乘逆元。
	fac[0]=1;
	for(int i=1;i<=S;i++) fac[i]=fac[i-1]*i%P;
	inv[S]=Qpow(fac[S],P-2);
	for(int i=S-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%P;
}

int C(int n,int m) {
	if(n<m) return 0;
	return fac[n]*inv[m]%P*inv[n-m]%P;
}

signed main() {
	pret();
	int h=R,w=R,k=R,cnt=C(h*w,k);
	int invcnt=Qpow(cnt,P-2);
	cnt=cnt*h%P*w%P; //这里就是计算的“选方格的情况总数”。
	for(int i=1;i<=h;i++) for(int j=1;j<=w;j++) {
		int lx=C((i-1)*w,k),ux=C((h-i)*w,k);
		int ly=C((j-1)*h,k),uy=C((w-j)*h,k);
		//计算上文提到条件满足的矩形的个数。
		//l 指的是 "lower",较小的;r 指的是 'upper',较大的。
		int lxly=C((i-1)*(j-1),k),uxuy=C((h-i)*(w-j),k);
		int lxuy=C((i-1)*(w-j),k),uxly=C((h-i)*(j-1),k);
		//容斥,去掉重复的部分。
		(cnt-=(lx+ly+ux+uy-lxly-uxuy-lxuy-uxly))%=P;
	}
	myio::print_int(((cnt*invcnt)%P+P)%P);
	//因为取模有可能是负数,因此要转化成自然数。
	return 0;
}
posted @ 2023-04-10 17:09  robinyqc  阅读(63)  评论(0)    收藏  举报