题意:
平面上有 n ( n ≤ 10 ) n(n\leq 10) n(n≤10)个矩形,每个矩形给出左下角坐标和右上角坐标。每次操作可以随机选择一个,把这个矩形的区域涂黑。给出一个矩形,左下角是 ( 0 , 0 ) (0,0) (0,0),右上角是 ( W , H ) (W,H) (W,H),求把这个矩形完全涂黑的期望操作次数
Solution:
显然这是个期望dp,但涂黑的矩形我们再涂没贡献,于是考虑状压记录他,设 d p [ s ] dp[s] dp[s]为涂黑的集合为 s s s时,期望把 ( 0 , 0 ) ( W , H ) (0,0)(W,H) (0,0)(W,H)这个矩形完全涂黑的操作次数,期望dp,考虑事件,对于每个 s s s,抽取的事件有两种,第一种抽到没涂过的,第二种抽到涂过的,没涂过的又细分几种,那么转移方程为
d p [ s ] = p o p c o u n t ( s ) n d p [ s ] + ∑ { i ∣ ( ( s > > i − 1 ) & 1 ) = = 0 } 1 n d p [ s ∣ ( 1 < < i − 1 ) ] + 1 dp[s]=\frac{popcount(s)}{n}dp[s]+\sum_{\{i|((s>>i-1)\&1)==0\}} \frac{1}{n}dp[s|(1<<i-1)]+1 dp[s]=npopcount(s)dp[s]+{i∣((s>>i−1)&1)==0}∑n1dp[s∣(1<<i−1)]+1
移项化简就可以得到dp[s]的转移方程了
接下来就是初值的设置,如果集合 s s s足以涂黑,那么 d p [ s ] = 0 dp[s]=0 dp[s]=0,如何确定一个集合能否涂黑?
我一开始的想法其实是做容斥,求出面积交是不是 ( W + 1 ) ( H + 1 ) (W+1)(H+1) (W+1)(H+1),但是这个容斥太难实现了。然后就是扫描线,感觉有点高射炮打蚊子,并且我已经忘记怎么写了。然后发现有种方法很妙,和容斥一样适合小规模数据,离散化+bitset暴力,直接用bitset模拟二维平面,然后检查是否完全覆盖,此时需要注意的是:不是对每个点打上标记,而是线段,否则会把这种情况判对
此时 W = 3 , H = 3 W=3,H=3 W=3,H=3, x ∈ [ 1 , 3 ] , y ∈ [ 1 , 3 ] x\in[1,3],y\in[1,3] x∈[1,3],y∈[1,3]的所有点都被覆盖,但是仍有空缺,正确做法是记录以某个点为开头/结尾的线段是否被覆盖
// #include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<cstdio>
#include<bitset>
#include<cstring>
#include<algorithm>
using namespace std;
using ll=long long;
const int N=200005,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353;
ll qpow(ll a,ll b)
{
ll ret=1,base=a;
while(b)
{
if(b&1) ret=ret*base%mod;
base=base*base%mod;
b>>=1;
}
return ret;
}
ll inv(ll x){return qpow(x,mod-2);}
struct node
{
int x,y;
friend istream& operator>>(istream& o,node& x)
{
scanf("%d%d",&x.x,&x.y);
return o;
}
}a[15][3];
int n;
ll w,h,f[1<<10];
vector<int>valx,valy;
bitset<25>bits[25];
void color(int x)
{
// printf("x=[%d,%d],y=[%d,%d]\n",a[x][1].x,a[x][2].x,a[x][1].y,a[x][2].y);
for(int i=a[x][1].x;i<a[x][2].x;i++)
for(int j=a[x][1].y;j<a[x][2].y;j++) bits[i][j]=true;
}
bool check(int s)
{
for(int i=1;i<=w;i++) bits[i]=bits[24];
for(int i=1;i<=n;i++)
if((s>>i-1)&1) color(i);
for(int i=1;i<w;i++)
for(int j=1;j<h;j++)
if(bits[i][j]==false) return false;
return true;
}
void work()
{
scanf("%d%lld%lld",&n,&w,&h);
valx.clear();
valy.clear();
valx.push_back(0);
valy.push_back(0);
valx.push_back(w);
valy.push_back(h);
for(int i=1;i<=n;i++)
{
cin>>a[i][1]>>a[i][2];
for(int j=1;j<=2;j++)
{
valx.push_back(a[i][j].x);
valy.push_back(a[i][j].y);
}
}
sort(valx.begin(),valx.end());
sort(valy.begin(),valy.end());
valx.erase(unique(valx.begin(),valx.end()),valx.end());
valy.erase(unique(valy.begin(),valy.end()),valy.end());
for(int i=1;i<=n;i++)
{
for(int j=1;j<=2;j++)
{
a[i][j].x=lower_bound(valx.begin(),valx.end(),a[i][j].x)-valx.begin()+1;
a[i][j].y=lower_bound(valy.begin(),valy.end(),a[i][j].y)-valy.begin()+1;
}
}
w=lower_bound(valx.begin(),valx.end(),w)-valx.begin()+1;
h=lower_bound(valy.begin(),valy.end(),h)-valy.begin()+1;
for(int s=0;s<(1<<n);s++) f[s]=1;
for(int s=0;s<(1<<n);s++)
{
if(f[s]==0) continue;
if(check(s))
{
f[s]=0;
for(int i=1;i<=n;i++)
{
if((s>>i-1)&1) continue;
f[s|(1<<i-1)]=0;
}
}
}
if(f[(1<<n)-1])
{
printf("-1\n");
return;
}
ll invtmp=inv(n);
for(int s=(1<<n)-2;s>=0;s--)
{
if(f[s]==0) continue;
ll cnt=__builtin_popcount(s);
for(int j=1;j<=n;j++)
{
if((s>>j-1)&1) continue;
f[s]=(f[s]+invtmp*f[s|(1<<j-1)]%mod)%mod;
}
f[s]=f[s]*n%mod*inv(n-cnt)%mod;
}
printf("%lld\n",f[0]);
}
int main()
{
int t; cin>>t;
while(t--) work();
return 0;
}