题目比较长,读题的时间会长一些
除去它颇有趣味的背景
在一个矩阵中选数,每行最多只能选一个,可以不选,
每列选择的数最多不能超过所选数字个数的一半,即向下取整,可以不选,
最终选数个数不能为零,求总方案数
值得注意的是,矩阵中的每个数字的值,也意味着不同的选择
也就是说,如果这个数为2,就指单选这个数,就有两种选择,对总方案数是乘法的贡献
显然,这是一个动态规划
比起行的要求,更难实现的是列的要求
正难则反,记录不满足列的要求的方案数
显然,如果一个方案数不满足列的要求,那么有且仅有一个列不满足列的要求
因为只能有一个列选择的数超过了所选数字的一半
则一定满足,这一列选择的数,大于其他列选择的数的和
动态规划的两个维度出来了
最后,再加一个维度记录行的序列数就可以了
这是一个容斥的想法,那么总方案数就是一个很容易的dp
g[i][j],表示前i行,选了j个数的方案数
g[0][0]=1;//什么都不选,方案数为1
for(ri i=1; i<=n; ++i)
for(ri j=0; j<=n; ++j) {
g[i][j]=g[i-1][j];//第i行第j列不选的方案数
if(j!=0)
g[i][j]=(g[i][j]+g[i-1][j-1]*s[i]%mo)%mo;
//加上第i行第j列选的方案数
}
不合法方案数,也是一个dp,注意:以差值优化
f[i][j],表示前i行,选数最多的那一列,比其它列选数总和多的值
for(ri k=1; k<=m; ++k) {
//枚举选数最多列的序列数
memset(f,0,sizeof(f));
f[0][n]=1;
//理性理解,特殊判断
//毕竟f[0][n]也是一种不合法方案
for(ri i=1; i<=n; ++i)
for(ri j=n-i; j<=n+i; ++j)
f[i][j]=(f[i-1][j]+
//没选第i行的数
f[i-1][j-1]*a[i][k]%mo+
//选第i行的数,且选择a[i][k]的数
f[i-1][j+1]*(s[i]-a[i][k]+mo)%mo)%mo;
//选第i行的数,且不选a[i][k]的数
for(ri i=1; i<=n; ++i) sum1=(sum1+f[n][n+i])%mo;
//记录不合法方案数
}
最终,时间复杂度 O(m*n^2)
另外友情提醒,数组随用随开,开得过大会TLE
或许因为这题卡时间线
over