1. 程式人生 > >Wannafly挑戰賽26 F.msc的棋盤 計數DP 最小割轉換

Wannafly挑戰賽26 F.msc的棋盤 計數DP 最小割轉換

題目描述

一天,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;
}