2021昆明icpc B 状压+期望dp,一点几何模拟

题意:

平面上有 n ( n ≤ 10 ) n(n\leq 10) n(n10)个矩形,每个矩形给出左下角坐标和右上角坐标。每次操作可以随机选择一个,把这个矩形的区域涂黑。给出一个矩形,左下角是 ( 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>>i1)&1)==0}n1dp[s(1<<i1)]+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值