题意:
平面上有n(n≤10)n(n\leq 10)n(n≤10)个矩形,每个矩形给出左下角坐标和右上角坐标。每次操作可以随机选择一个,把这个矩形的区域涂黑。给出一个矩形,左下角是(0,0)(0,0)(0,0),右上角是(W,H)(W,H)(W,H),求把这个矩形完全涂黑的期望操作次数
Solution:
显然这是个期望dp,但涂黑的矩形我们再涂没贡献,于是考虑状压记录他,设dp[s]dp[s]dp[s]为涂黑的集合为sss时,期望把(0,0)(W,H)(0,0)(W,H)(0,0)(W,H)这个矩形完全涂黑的操作次数,期望dp,考虑事件,对于每个sss,抽取的事件有两种,第一种抽到没涂过的,第二种抽到涂过的,没涂过的又细分几种,那么转移方程为
dp[s]=popcount(s)ndp[s]+∑{i∣((s>>i−1)&1)==0}1ndp[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]的转移方程了
接下来就是初值的设置,如果集合sss足以涂黑,那么dp[s]=0dp[s]=0dp[s]=0,如何确定一个集合能否涂黑?
我一开始的想法其实是做容斥,求出面积交是不是(W+1)(H+1)(W+1)(H+1)(W+1)(H+1),但是这个容斥太难实现了。然后就是扫描线,感觉有点高射炮打蚊子,并且我已经忘记怎么写了。然后发现有种方法很妙,和容斥一样适合小规模数据,离散化+bitset暴力,直接用bitset模拟二维平面,然后检查是否完全覆盖,此时需要注意的是:不是对每个点打上标记,而是线段,否则会把这种情况判对
此时W=3,H=3W=3,H=3W=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;
}