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;
}

浙公网安备 33010602011771号