「JOI Open 2016」摩天大楼(笛卡尔树dp+优化)

Posted coldchair

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「JOI Open 2016」摩天大楼(笛卡尔树dp+优化)相关的知识,希望对你有一定的参考价值。

https://loj.ac/problem/2743

先排序,从小到大考虑。

最暴力直接设(f[i][j][k][u])表示前i个数,分成j段有顺序的,和差一共为k,首尾确定了u个的方案数。

复杂度是(O(n^3*A))

没有用到(L<=1000)

假设在每一段的附近都加上了(a[i]),可以得到这个和是非递减的,每次加(+(?a)*(2j-u))

最后这个和就是实际的相邻差,所以它要一直(<=L),复杂度(O(n^2L))

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const int mo = 1e9 + 7;

const int N = 105, M = 1005;

int n, m, a[N];

ll f[2][N][M][3], o;

void add(int j, int k, int u, ll q) {
    f[!o][j][k][u] += q;
}

int main() {
    scanf("%d %d", &n, &m);
    fo(i, 1, n) scanf("%d", &a[i]);
    if(n == 1) {
        pp("1
"); return 0;
    }
    sort(a + 1, a + n + 1);
    f[o][0][0][0] = 1;
    fo(i, 1, n) {
        memset(f[!o], 0, sizeof f[!o]);
        int det = a[i] - a[i - 1], p = a[i];
        fo(j, 0, i) fo(k, 0, m) fo(u, 0, 2) if(f[o][j][k][u]) {
            f[o][j][k][u] %= mo;
            int nk = k + det * (2 * j - u);
            if(nk > m) continue;
            ll q = f[o][j][k][u];
            add(j + 1, nk, u, q * (j - u + 1));
            if(u < 2) add(j + 1, nk, u + 1, q * (1 + (!u)));
            if((j > u || i == n) && u < 2) add(j, nk, u + 1, q * (1 + (!u)));
            add(j, nk, u, q * (2 * j - u));
            if(j > 1 && !(j == 2 && u == 2 && i < n))
                add(j - 1, nk, u, q * (j - 1));
        }
        o = !o;
    }
    ll ans = 0;
    fo(k, 0, m) ans = (ans + f[o][1][k][2]) % mo;
    pp("%lld
", ans);
}

以上是关于「JOI Open 2016」摩天大楼(笛卡尔树dp+优化)的主要内容,如果未能解决你的问题,请参考以下文章

题解[JOI Open 2021] Crossing

1014

BZOJ4388JOI2012 invitation 堆+线段树+并查集模拟Prim

Joi 嵌套模式

笛卡尔树

笛卡尔树