code
题意
给定一个M*N 的矩阵,矩阵中每个数都是一个 K 位 2 进制数,把矩阵每一行的数都或起来,得到 N 个数,把每一列的数都或起来,得到 M 个数,让得到的n+m个数都等于2k-1。
样例
in
多组测试数据,在输入的第一行会给出测试数据组数 T 。
每组测试数据一共一行,为三个整数N,M,K。
4 1 1 31 1 10 2 2 2 1 2 3 2
out
输出每组数据一行,即答案。
1 1 7 625
闲扯
一共三个半小时的考试,在这题上花了俩还多
而且 竟然爆零了
而且 听人家AC的讲题没听懂
而且 听听懂的人讲题还没懂
我这种蒟蒻是不是没救了
最后照着AC代码模拟。。。
然后
如果发现有不合理的地方,欢迎指出
如果发现有不合理的地方,欢迎指出
如果发现有不合理的地方,欢迎指出
思路
因为二进制的每一位是互相独立的,只需要算出m*n的矩阵或起来是1的方案数
最后算k次方
容斥原理
总情况2^(n*m)
总情况中包含“一行零列”这种不合法情况
需要减去这种情况
同时总情况中也包含“零行一列”这种不合法情况
需要减去这种情况
但是减去这两项的话
“一行一列”这种情况就被减了两次
需要再加一次
同理
由于“i-1行j列”和“i行j-1列”的重复计算
使得“i行j列”的情况多减了,需要与上述运算的符号相反
即:i,j组合的符号是根据上面的计算推出来的
res:仅考虑i行j列中没有1的情况总数
C(n,i)*C(m,j)是把行列选出来
Pow(2^(n*m-i*m-j*n+i*j))是说:剩下的格子随意,当前仅考虑选出的i行j列中没有1
代码
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define N 100005 #define LL long long #define mod 1000000007 long long a[55]; void pre(){ a[0]=1; for(int i=1;i<=50;i++){ a[i]=a[i-1]*i%mod; } } inline void putout(long long x){ char c[15]; int k=0; if(x<0) putchar('-'),x=-x; do { c[++k]=x%10+48; x/=10; } while(x); while(k) putchar(c[k--]); } inline long long fastpow(long long now,int k){ long long mul=now; long long ret=1LL; while(k){ if(k%2){ ret=ret*mul%mod; } mul=mul*mul%mod; k>>=1; } return ret; } long long C(int n,int m){ LL ret=1LL*(a[n]*fastpow(a[m],mod-2)%mod)* fastpow(a[n-m],mod-2)%mod; return ret; } int main(){ freopen("code.in","r",stdin); freopen("code.out","w",stdout); int T; pre(); scanf("%d",&T); while(T--){ int n,m,k; scanf("%d%d%d",&n,&m,&k); long long ans=0; for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++){ int plus=((i+j)%2) ? -1:1; long long fast=fastpow(2,n*m-i*m-j*n+i*j); long long res=(1LL*C(n,i)*C(m,j)%mod)*fast%mod; ans=(ans+res*plus)%mod; } } ans=(ans+mod)%mod; ans=fastpow(ans,k); printf("%I64d ",ans); } return 0; }