Wannafly挑战赛26 F.msc的棋盘 计数DP 最小割转换

14 篇文章 0 订阅
11 篇文章 0 订阅

链接: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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值