题意:有多少个 N ∗ M N *M N∗M 的矩阵满足:其所有数字都是 K K K 位 2 2 2 进制数,且每一行每一列的或都是 2 K − 1 2^K-1 2K−1。
首先对于二进制的题很大一部分是逐位处理,因为各二进制位之间互不影响,首先我们注意到 K K K 是没有意义的, 我们只要求出 N ∗ M N * M N∗M的 0 − 1 0-1 0−1 矩阵有多少种方案使得每行每列的或都是 1 1 1即可,记这个答案为 a n s ′ ans' ans′的话, 最后答案即为 a n s = a n s ′ K ans=ans'^K ans=ans′K。
我们设 d p [ i ] [ j ] dp[i][ j] dp[i][j]表示前 i i i 行有 j j j 列或为 1 1 1,那么 d p [ i ] [ j ] dp[i][ j] dp[i][j]转移到下一行时, 能转移到 d p [ i + 1 ] [ k ] , ( k ≥ j ) dp[i+1][k]\ ,\ (k≥j) dp[i+1][k] , (k≥j)
当 k = j k=j k=j 时, 意味着在 i + 1 i+1 i+1行上所有前 i i i行为 0 0 0位置是 0 0 0, 否则一定会出现新的列或为 1 1 1,而第 i + 1 i+1 i+1行剩下 j j j列可以随便放,但要注意不能全放 0 0 0,排除这种情况,方程即为 d p [ i + 1 ] [ j ] + = d p [ i ] [ j ] ∗ ( 2 j − 1 ) dp[i+1][ j]+=dp[i][j]*(2^j-1) dp[i+1][j]+=dp[i][j]∗(2j−1)。
当 k > j k>j k>j 时, 意味着 i + 1 i+1 i+1行在前 i i i行为 0 0 0的位置上有 k − j k-j k−j列是1,此时第 i + 1 i +1 i+1行剩下 j j j列可以随便放(剩下 j j j列全是 0 0 0也可以), 方程 为 d p [ i + 1 ] [ k ] + = d p [ i ] [ j ] ∗ 2 j ∗ C k − j m − j 为dp[i+1][k]+=dp[i][j]*2^j*C^{m-j}_{k-j} 为dp[i+1][k]+=dp[i][j]∗2j∗Ck−jm−j。预处理组合数和 2 2 2的 i i i次幂,最后用快速幂即可。
d p dp dp的初值就是在第一行时从 m m m列中选 i i i位为 1 1 1的方案数即 d p [ 1 ] [ i ] = C i m dp[1][i]=C^m_i dp[1][i]=Cim
```#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,m,kk,t;
ll c[55][55],dp[55][55],pow_2[55];
ll ksm(ll q,ll w)
{
ll h=1;
while(w)
{
if(w&1)
h=h*q%mod;
q=q*q%mod;
w>>=1;
}
return h;
}
int main()
{
cin>>t;
pow_2[0]=1;
for(int i=1;i<=50;++i)
pow_2[i]=(pow_2[i-1]%mod*2%mod)%mod;
for(int i=1;i<=50;++i)
{
c[i][i]=1;
c[i][0]=1;
}
for(int i=1;i<=50;++i)
for(int j=i+1;j<=50;++j)
c[j][i]=(c[j-1][i]%mod+c[j-1][i-1]%mod)%mod;
while(t--)
{
cin>>n>>m>>kk;
memset(dp,0,sizeof(dp));
for(int i=0;i<=m;i++)
dp[1][i]=c[m][i];
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
dp[i+1][j]=(dp[i+1][j]%mod+(dp[i][j]%mod*(pow_2[j]-1)%mod)%mod)%mod;
for(int k=j+1;k<=m;++k)
dp[i+1][k]=(dp[i+1][k]%mod+(dp[i][j]%mod*c[m-j][k-j]%mod*pow_2[j]%mod)%mod)%mod;
}
cout<<ksm(dp[n][m],kk)<<endl;
}
return 0;
}