1 条题解

  • 0
    @ 2021-11-19 9:42:29

    容斥好题。

    题目条件有行列和对角线,不是很好处理。

    考虑如果暴力容斥,直接枚举让那些行、列、对角线变成全是列,时间复杂度为 O(22n)O(2^{2n}) 直接爆炸。

    考虑将总概率减去每一行列对角线都有至少一个 00 的概率。

    枚举选取列和对角线的情况,这样我们就可以知道一些位置必须要选 11

    那么由于一定不能有一行满足全是 11,所以剩下的地方只要不是一行全都是 11 就可以了。

    si,Ss_{i,S} 表示在第 ii 行选取的列的集合为 SS 的概率,预处理时间复杂度 O(n×2n)O(n\times 2^n)

    枚举列与对角线的状态之后,设 fi,Sf_{i,S} 表示前 ii 行当前选取集合为 SS 的列的概率。

    $$f_{i,S}=f_{i-1,S}\times s_{i,SS}\times (1-s_{i,U\oplus SS}) $$

    其中 SSSS 表示算上对角线后,当前这一行必须天填 11 的代价,UU 表示全集。


    代码
    #include<bits/stdc++.h>
    using namespace std;
    const int N=21,maxS=(1<<N)+5,mod=31607;
    int n,a[N+5][N+5],lg2[maxS],cnt[maxS],f[maxS],sum[N+5][maxS],ans;
    int kuai(int a,int b){//快速幂
    	int ans=1;
    	for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;
    	return ans;
    }
    int main(){
    	scanf("%d",&n);
    	int Inv=kuai(10000,mod-2);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			scanf("%d",&a[i][j]),a[i][j]=1ll*Inv*a[i][j]%mod;
    	for(int i=0;i<n;i++)lg2[1<<i]=i+1;
    	for(int i=0;i<1<<n;i++)cnt[i]=cnt[i>>1]+(i&1);//预处理二进制中1的数量
    	for(int i=1;i<=n;i++){
    		sum[i][0]=1;
    		for(int S=1;S<1<<n;S++){
    			int x=S&-S;
    			sum[i][S]=1ll*sum[i][S^x]*a[i][lg2[x]]%mod;
    		}
    	}
    	for(int T=0;T<4;T++){
    		for(int S=0;S<1<<n;S++)if((cnt[S]+cnt[T])&1)f[S]=mod-1;//容斥系数
    		else f[S]=1;
    		for(int i=1;i<=n;i++){
    			for(int S=0;S<1<<n;S++){
    				int SS=S;
    				if(T&1)SS|=(1<<(i-1));
    				if(T&2)SS|=(1<<(n-i));
    				f[S]=1ll*f[S]*sum[i][SS]%mod*(1-sum[i][((1<<n)-1)^SS]+mod)%mod;
    			}
    		}
    		for(int S=0;S<1<<n;S++)ans=(ans+f[S])%mod;
    	}
    	printf("%d",(1-ans+mod)%mod);
    }
    
    • 1

    信息

    ID
    40
    时间
    1000ms
    内存
    256MiB
    难度
    (无)
    标签
    递交数
    0
    已通过
    0
    上传者