bzoj2616

树形dp+笛卡尔树+单调栈

这道题跟树形dp有什么关系?

事实上,我们对矩形建立笛卡尔树,先找出最矮的矩形,向两边区间最矮的矩形连边,这样就构成了一棵二叉树,因为只有一个矮的区间会对高的区间造成影响,而且儿子之间不会互相影响,并且这样一层一层保证了每段矩形都会被覆盖到,其实就是单调栈,所以这样连是对的,然后跑一个树形背包,dp[i][j]表示i节点子树放了j个车,很明显两个儿子之间不会互相影响,所以自然是可以合并儿子之间的信息。

f[u][i]表示自己不放儿子放的方案数,dp[u][i]表示子树里放i个的方案数,然后转移一下就行了。一个 n*m的矩形内放k个的方案数是k!*C(n,k)*C(m,k),选完行和列的交点后全排列表示所有交点。

#include<bits/stdc++.h>
using namespace std;    
typedef long long ll;
const int N = 505, mod = 1e9 + 7;
int n, K, root;
int H[N], h[N], lc[N], rc[N], w[N];
ll dp[N][N], fac[1000005], f[N][N];
void up(ll &x, const ll &t) { x = (x + t) % mod; }
ll power(ll x, ll t)
{
    ll ret = 1;
    for(; t; t >>= 1, x = x * x % mod) if(t & 1) ret = ret * x % mod;
    return ret;
}
ll inv(ll x) { return power(x, mod - 2); }
ll C(int a, int b) 
{
    if(a < b) return 0;
    return fac[a] * inv(fac[b]) % mod * inv(fac[a - b]) % mod; 
}
ll calc(int a, int b, int K) 
{ 
    if(a < K || b < K) return 0;
    ll ret = fac[K] * C(a, K) % mod * C(b, K) % mod; 
    return ret;
}
void dfs(int u)
{
    f[u][0] = dp[u][0] = 1;
    if(!u) return;
    dfs(lc[u]);
    dfs(rc[u]);
    for(int i = 1; i <= K; ++i)
        for(int j = 0; j <= i; ++j)
            up(f[u][i], dp[lc[u]][j] * dp[rc[u]][i - j] % mod);
    for(int i = K; i >= 1; --i)
        for(int j = 0; j <= i; ++j) if(f[u][j])
            up(dp[u][i], f[u][j] * calc(H[u], w[u] - j, i - j) % mod);
}
int build(int l, int r)
{
    if(l > r) return 0;
    int p = l;
    for(int i = l; i <= r; ++i) if(h[i] < h[p]) p = i;
    lc[p] = build(l, p - 1);
    rc[p] = build(p + 1, r);
    H[lc[p]] = h[lc[p]] - h[p];
    H[rc[p]] = h[rc[p]] - h[p];
    w[p] = r - l + 1;
    return p; 
}
int main()
{
    scanf("%d%d", &n, &K);
    fac[0] = 1;
    for(int i = 1; i <= n; ++i) scanf("%d", &h[i]), H[i] = h[i];
    for(int i = 1; i <= 1000000; ++i) fac[i] = fac[i - 1] * (ll)i % mod;
    root = build(1, n);
    dfs(root);
    printf("%lld\n", dp[root][K]);
    return 0;
}
View Code

 

posted @ 2017-10-31 11:09  19992147  阅读(264)  评论(0编辑  收藏  举报