Wannafly挑战赛26 F.msc的棋盘 计数DP 最小割转换
链接:https://www.nowcoder.com/acm/contest/212/F
来源:牛客网
题目描述
一天,msc在家里找到了一个n×m的棋盘。
这个棋盘十分奇特,每个格子最多放一个棋子,但是你并不能看见具体的棋子都放在了哪些地方,但是有一个显示屏可以显示每一行每一列有多少棋子。
然而遗憾的是,由于棋盘已经放了很久,现在显示每一行有多少棋子的部分已经坏掉了,所以msc只能知道现在的棋盘上面每一列有多少棋子。
由于msc是一个有着强烈求知欲的女生,所以她希望知道显示屏坏掉的部分有多少种不同的可能的显示情况。
两种显示情况不同,当且仅当存在至少一行在两种情况中显示的数值是不同的。
msc是解决不了这么复杂的问题的,所以她告诉了你n,m以及b[1..m]表示每一列的棋子个数,请你帮她求出可能的方案数,由于答案可能很大,请将答案对1,000,000,007取模后再输出。
输入描述:
第一行两个整数n和m,分别表示棋盘的行数和棋盘的列数。
第二行m个整数b[1..m],第i个数表示棋盘中第i列上的棋子个数。
输出描述:
一行一个整数表示答案。
题解:
直接做无法下手。
贴个题解
代码:
#include <bits/stdc++.h>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif
#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 5e5 + 5;
int b[MAXN];
int preb[MAXN];
int least[MAXN];
ll fastpow (int a, int n) {
ll ret = 1, base = a;
while (n) {
if (n & 1) ret = ret * base % MOD;
base = base * base % MOD;
n >>= 1;
}
return ret;
}
ll d[55][55][55 * 55];
ll fac[112], inv[122];
ll mul[123][123];
int main() {
#ifdef LOCAL
freopen ("input.txt", "r", stdin);
#endif
int n, m;
scanf ("%d %d", &n, &m);
int sum = 0;
for (int i = 1; i <= m; i++) {
scanf ("%d", &b[i]);
sum += b[i];
}
for (int i = 111; i >= 0; i--) {
mul[i][i] = i;
mul[i][i + 1] = 1;
for (int j = i - 1; j >= 0; j--) mul[i][j] = mul[i][j + 1] * j % MOD;
}
fac[0] = 1;
for (int i = 1; i <= 111; i++) fac[i] = fac[i - 1] * i % MOD;
inv[111] = fastpow (fac[111], MOD - 2);
for (int i = 111 - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;
// debug(inv[0])
sort (b + 1, b + 1 + m);
for (int i = 1; i <= m; i++) preb[i] = preb[i - 1] + b[i];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
least[i] = max (least[i], sum - (n - i) * (m - j) - preb[j]);
for (int i = 0; i <= n; i++) if (!least[i]) d[0][i][0] = 1;
for (int i = 0; i < m; i++) {
for (int j = 0; j <= n; j++) {
for (int k = least[j]; k <= sum; k++) {
if (!d[i][j][k]) continue;
for (int l = 0; l <= n - j && k + l * (i + 1) <= sum; l++) {
if (least[j + l] <= k + l * (i + 1) ) {
d[i + 1][j + l][k + l * (i + 1)] += d[i][j][k] * mul[j + l][j + 1] % MOD * inv[l] % MOD;
d[i + 1][j + l][k + l * (i + 1)] %= MOD;
}
}
}
}
}
printf ("%lld\n", d[m][n][sum]);
return 0;
}