Codeforces 1530F. Bingo

一年一更任务达成√

题目链接:F - Bingo

题目大意:给定一个 \(n\times n(n\le 21)\) 表格,表格中每个元素有 \(p_{i,j}\) 的概率为 \(1\),否则为 \(0\)。求至少有一行或一列或一条对角线全为 \(1\) 的概率,其中对角线指两条主副对角线。

考虑一个 naive 的容斥想法,直接容斥有多少条(行/列/对角线)是不全为 \(1\) 的,并进行计算。

此时发现不全为 \(1\) 的计算很繁,于是考虑求原题的补集。即求所有(行/列/对角线)均不全为 \(1\)(或者说均存在 \(0\))的概率,那么容斥之后则是求钦定某些(行/列/对角线)均全为 \(1\) 的概率,这个概率可以非常直接地算出来:把所有被钦定为 \(1\) 的位置对应的 \(p_{i,j}\) 乘起来即可。

那么采用这种做法,复杂度则是 \(O(n^22^{2n})\) 级别的,显然无法接受。

考虑一个神奇的想法,把容斥计算拆成两部分。假设我们已经钦定了有哪些列或对角线是全为 \(1\) 的,观察下我们计算的是什么。

我们的式子本来是所有被钦定为 \(1\) 的位置的 \(p_{i,j}\) 之积,而现在在这一前提下,则变成我们要再次钦定有哪些行是全为 \(1\) 的,并计算 在第一轮钦定中变为 \(1\)\(p_{i,j}\) 之积乘上(枚举第二轮钦定哪些行全为 \(1\),并计算容斥系数乘上在第二轮钦定中新成为 \(1\)\(p_{i,j}\) 之积的和)。

而对于括号内的部分,对应式子的意义等同于求每一行均不全为 \(1\) 的概率。这时我们发现,这里的每一行相互之间是独立的,所以每行的贡献可以分开算,最后再乘起来就好。

于是我们考虑对每一行预处理在钦定了些列或对角线为 \(1\) 后,该行不全为 \(1\) 的概率。注意到钦定某些位置为一相当于修改对应的 \(p_{i,j}\) 使其变为 \(1\)。所以把对应行 \(p_{i,j}\) 的乘积直接除掉对应列集合的乘积即可,这玩意可以直接利用 lowbit 转移 \(O(2^n)\) 求出来,而对于对角线的部分直接枚举四种情况分别做就好。这样时间复杂度是 \(O(n2^n)\) 的,足以通过此题。

在实现时,可以先 \(2^2\) 枚举对角线的情况,再容斥列,这样实现起来会方便些。

#include<bits/stdc++.h>
using namespace std;
#define MOD 31607
#define N 21
int n,m,ans,a[N][N],b[N][N],f[N][1<<N],c[N],g[1<<N],k[1<<N],Lg[1<<N];
int lowbit(int x){return x&(-x);}
int sol()
{
	int res=0;
	for(int i=0;i<n;i++){
		f[i][0]=1;
		for(int j=1;j<=m;j++)
			f[i][j]=f[i][j^lowbit(j)]*b[i][Lg[lowbit(j)]]%MOD;
		for(int j=0;j<=m;j++)f[i][j]=(MOD+1-f[i][j])%MOD;
	}
	for(int j=0;j<=m;j++)
		for(int i=n-2;i>=0;i--)
			(f[i][j]*=f[i+1][j])%=MOD;
	for(int i=0;i<=m;i++)
		res=(res+f[0][m^i]*g[i]%MOD*k[i])%MOD;
	return res;
}
int pre(int o1,int o2)
{
	int K=1;
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
		b[i][j]=a[i][j];
	if(o1)for(int i=0;i<n;i++)(K*=b[i][i])%=MOD,b[i][i]=1;
	if(o2)for(int i=0;i<n;i++)(K*=b[i][n-i-1])%=MOD,b[i][n-i-1]=1;
	for(int i=0;i<n;i++)c[i]=1;
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
		c[j]=c[j]*b[i][j]%MOD;
	g[0]=1;
	for(int i=1;i<=m;i++)
		g[i]=g[i^lowbit(i)]*c[Lg[lowbit(i)]]%MOD;
	return K*sol()%MOD;
}
int main()
{
	k[0]=1;
	for(int i=0,o=1;i<N;i++,o<<=1)Lg[o]=i;
	for(int i=1;i<(1<<N);i++)k[i]=MOD-k[i^lowbit(i)];
	scanf("%d",&n),m=(1<<n)-1;
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++){ 
		scanf("%d",&a[i][j]);
		(a[i][j]*=3973)%=MOD;
	}
	for(int i=0;i<2;i++)
	for(int j=0;j<2;j++)
		ans=(ans+(i^j?MOD-1:1)*pre(i,j))%MOD;
	printf("%d\n",(MOD+1-ans)%MOD);
}
posted @ 2024-04-25 03:59  DeaphetS  阅读(21)  评论(0编辑  收藏  举报