ICPC 2022 KM B

题目链接
首先是判断每个子集是否覆盖了整个矩形,离散化后暴力做是 \(O(2^nT*400)\),会TLE。当时队友想了一个挺巧妙的容斥的做法,但写起来比较复杂。其实发现就是一个01取并集的过程,可以直接bitset优化掉32倍的常数就ok了!

然后是状态转移,其实是比较经典的问题:有向图第一次走到合法的点的期望。但当时沿用了从起点开始,记录概率和期望的思路,结果碰到了一些麻烦就没调出来(后来冷静想了一下应该还是可以做的)

但从终点(合法点)往前考虑就会方便很多:直接DP考虑每个点出发的期望,从后往前DP即可!

就是要跳出从起点往后做的思维,考虑起点第一次到合法状态的期望这个问题:发现起点的问题可以转化为第一步到达的点的问题,所以就直接把状态设计为该点出发第一次到合法状态的期望!

#include<bits/stdc++.h>
using namespace std;
const int N=25,P=998244353;
int fpw(int a,int x){
	int s=1;
	for(;x;x>>=1,a=1ll*a*a%P) if(x&1) s=1ll*s*a%P;
	return s;
}
void discretize(int& mx,int* a,int* b,int n){
	vector<int>h;
	h.push_back(0);
	h.push_back(mx);
	for(int i=1;i<=n;i++){
		//a[i]++;
		a[i]=min(a[i],mx);
		b[i]=min(b[i],mx);
		h.push_back(a[i]);
		h.push_back(b[i]);
	}
	sort(h.begin(),h.end());
	vector<int>:: iterator it=unique(h.begin(),h.end());
	h.erase(it,h.end());
	//for(int i=0;i<h.size();i++) cout<<h[i]<<" "; puts("");
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(h.begin(),h.end(),a[i])-h.begin()+1;
		b[i]=lower_bound(h.begin(),h.end(),b[i])-h.begin();
	}
	mx=h.size()-1;
}
int n,W,H,x[N],y[N],x2[N],y2[N],iv[N];
bitset<512>ss[N],sg[1<<10];
int f[1<<10];

void work(){
	cin>>n>>W>>H;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&x[i],&y[i],&x2[i],&y2[i]);
		iv[i]=fpw(i,P-2);
	}
	discretize(W,x,x2,n);
	discretize(H,y,y2,n);
	//return;
	/*cout<<W<<" "<<H<<endl;
	for(int i=1;i<=n;i++)
		printf("%d %d %d %d\n",x[i],x2[i],y[i],y2[i]);
		*/
	//return;
	for(int i=1;i<=n;i++){
		ss[i].reset();
		for(int j=x[i];j<=x2[i];j++){
			for(int k=y[i];k<=y2[i];k++){
				ss[i][(j-1)*H+k-1]=1;
			}
		}
	}
	for(int i=0;i<(1<<n);i++) sg[i].reset();
	for(int i=0;i<(1<<n);i++){
		for(int j=1;j<=n;j++) if(!(i&(1<<(j-1)))){
			int t=i+(1<<(j-1));
			if(!sg[t].count()) sg[t]=sg[i]|ss[j];
		}
	}
	if(sg[(1<<n)-1].count()!=W*H){
		puts("-1");
		return;
	}
	for(int i=(1<<n)-1;~i;i--){
		if(sg[i].count()==W*H){
			f[i]=0;
			continue;
		}
		int t=0,s=0;
		for(int j=1;j<=n;j++){
			if(i&(1<<(j-1))) t++;
			else (s+=f[i+(1<<(j-1))])%=P;
		}
		f[i]=1ll*(s+n)*iv[n-t]%P;
	}
	cout<<f[0]<<endl;
}
int main()
{
	//srand(time(0));
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	int T; cin>>T; while(T--) work();
	return 0;
}
posted @ 2022-07-13 12:01  sz[sz]  阅读(33)  评论(0)    收藏  举报