【WC2016模拟】Fountain

Description:

【WC2016模拟】Fountain_第1张图片
1<=n<=40,1<=d<=10^5,1<=ri<=40

题解:

傻了傻了,考场竟然没做出这道题。

《ZJOI波浪》是这题的加强版。

d是没有什么卵用的。

考虑把放的喷泉压到最紧,则长度= n1i=1max(r[i]r[i+1])

套个挡板问题即可求出在长度d下的方案数。

现在这个dp很容易想到用笛卡尔树去dp。

把r排好序。

fi,j,k 表示已经做了前i个,有j棵笛卡尔树,和是k的方案数。

对于新加入的一个数,考虑新开一棵树,或把一棵树作为它的左节点或右节点,或合并两棵树,方程显然。

Code:

#include
#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int mo = 1e9 + 7;

int n, d, a[45], f[41][41][1601];
ll fac[100005];

ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}

ll C(ll n, ll m) { return fac[n] * ksm(fac[m] * fac[n - m] % mo, mo - 2) % mo;}

int main() {
    fac[0] = 1; fo(i, 1, 1e5) fac[i] = fac[i - 1] * i % mo;
    scanf("%d %d", &n, &d);
    fo(i, 1, n) scanf("%d", &a[i]);
    sort(a + 1, a + n + 1);
    f[0][0][0] = 1;
    fo(i, 0, n - 1) {
        fo(j, 0, i) {
            fo(k, 0, 1600) if(f[i][j][k]) {
                 if(j > 0) f[i + 1][j][k + a[i + 1]] = (f[i + 1][j][k + a[i + 1]] + (ll) j * 2 * f[i][j][k] % mo) % mo;
                 if(j > 1) f[i + 1][j - 1][k + a[i + 1] * 2] = (f[i + 1][j - 1][k + a[i + 1] * 2] + (ll) j * (j - 1) * f[i][j][k] % mo) % mo;
                 f[i + 1][j + 1][k] = (f[i + 1][j + 1][k] + f[i][j][k]) % mo;
            }
        }
    }
    ll ans = 0;
    fo(k, 1, min(d, 1600))
        ans += C(d - k + (n + 1) - 1, n) * f[n][1][k - 1] % mo;
    printf("%lld", ans % mo);
}

你可能感兴趣的:(信息学,动态规划,组合数,笛卡尔树,动态规划,笛卡儿树)