【bzoj2616】SPOJ PERIODNI 树形DP

Description

【bzoj2616】SPOJ PERIODNI 树形DP_第1张图片

Input

第1行包括两个正整数N,K,表示了棋盘的列数和放的车数。
第2行包含N个正整数,表示了棋盘每列的高度。

Output

包括一个非负整数,表示有多少种放置的方案,输出答案mod
1000000007后的结果即可。

Sample Input

5 2 

2 3 1 2 4 

Sample Output

43 

HINT

对于100%的数据,有 N≤500,K≤500,h[i] ≤1000000。

Source

感谢CA爷提供的zky的代码

妈呀这是树形DP

从下往上看 按行划分,显示出一个树的结构。这样每个节点代表一个完整的矩形,它上面分出来的矩形是它的儿子。

每个节点有它的行与列。dp[u][i]表示u的子树中放i个车的方案数。则转移是这样:

dp[u][i]=dp[son][ik]calc(u,k)

其中 calc(u,k) 表示u节点放k个车的方案数,通过推倒可以发现:

calc(u,k)=k!CknCkm(ik)

其中n是u节点的行数,m是列数。因为儿子要占i-k列,所以要减去。

因为行数可能很大,所以 Ckn 要递归到每一层都处理一遍,具体到实现中, k!Ckn 可以化简。

我转左儿子右兄弟出问题了……简直了 还是不转比较好

然后关于这个题的玄学常数问题……

卡之前:
这里写图片描述
之后:
这里写图片描述

下面的是zky的代码,他卡的比我还狠…我就在转移的时候判了个是否为0…

这题我调了两天,龙哥好像随便写写就A了……太神了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

typedef long long LL;
const int INF = 1000000010;
const int SZ = 1000010;
const int mod = 1000000007;

int n,k;

int head[SZ],nxt[SZ],to[SZ];

void build(int f,int t)
{
    static int tot = 1;
    to[++ tot] = t;
    nxt[tot] = head[f];
    head[f] = tot; 
}

int a[SZ],sz = 1;

int h[SZ],len[SZ],S[SZ],L[SZ],R[SZ];

void build(int now,int l,int r)
{
    int minx = INF;
    for(int i = l;i <= r;i ++)
        minx = min(minx,a[i]);
    for(int i = l;i <= r;i ++)
        a[i] -= minx;
    h[now] = minx; len[now] = r - l + 1;

    int top = 0;
    S[++ top] = l - 1;
    for(int i = l;i <= r;i ++)
        if(a[i] == 0) S[++ top] = i;
    S[++ top] = r + 1;

    for(int i = 1;i < top;i ++)
    {
        if(S[i] + 1 <= S[i + 1] - 1)
        {
            ++ sz;
            build(now,sz);
            L[sz] = S[i] + 1; R[sz] = S[i + 1] - 1;
        }
    }
    for(int i = head[now];i;i = nxt[i])
        build(to[i],L[to[i]],R[to[i]]);
}

int f[1010][1010],CC[SZ],C[1010][1010];

void dfs(int u,int fa)
{
    f[u][0] = 1;
    for(int i = head[u];i;i = nxt[i])
        dfs(to[i],u);

    CC[0] = 1;
    for(int i = 1;i <= k;i ++) CC[i] = (LL)CC[i - 1] * (h[u] - i + 1) % mod;

    for(int i = k;i >= 0;i --)
        if(f[u][i]) 
            for(int j = 1;j + i <= k;j ++)
                f[u][i + j] = (f[u][i + j] + (LL)f[u][i] * C[len[u] - i][j] % mod * CC[j] % mod) % mod;
    if(fa)
        for(int i = k;i >= 0;i --)
            if(f[fa][i])
                for(int j = 1;j + i <= k;j ++)
                    f[fa][i + j] = (f[fa][i + j] + (LL)f[fa][i] * f[u][j] % mod) % mod;
}

void init()
{
    C[0][0] = 1;
    for(int i = 1;i <= 500;i ++)
    {
        C[i][0] = 1;
        for(int j = 1;j <= i;j ++)
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
}

int main()
{
// freopen("2616.in","r",stdin);
// freopen("2616.out","w",stdout);
    init();
    scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&a[i]);
    L[1] = 1; R[1] = n;
    build(1,1,n);
    dfs(1,0);
    printf("%d\n",f[1][k]);
    return 0;
}

你可能感兴趣的:(dp)