dp Code QBXT Test Ⅲ T2

17 篇文章 0 订阅

题意:有多少个 N ∗ M N *M NM 的矩阵满足:其所有数字都是 K K K 2 2 2 进制数,且每一行每一列的或都是 2 K − 1 2^K-1 2K1

首先对于二进制的题很大一部分是逐位处理,因为各二进制位之间互不影响,首先我们注意到 K K K 是没有意义的, 我们只要求出 N ∗ M N * M NM 0 − 1 0-1 01 矩阵有多少种方案使得每行每列的或都是 1 1 1即可,记这个答案为 a n s ′ ans' ans的话, 最后答案即为 a n s = a n s ′ K ans=ans'^K ans=ansK

我们设 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] , (kj)

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](2j1)

k > j k>j k>j 时, 意味着 i + 1 i+1 i+1行在前 i i i行为 0 0 0的位置上有 k − j k-j kj列是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]2jCkjmj。预处理组合数和 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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值