SP3734 PERIODNI

题意:

戳这里

分析:

一看到是直方图的题目,我们可以联想到笛卡尔树

我们将列数作为 BST 的一维,将高度作为小根堆的一维,这样笛卡尔树上每一个节点都是一个矩形

我们考虑在矩形中选出 k 个合法点的方案数,显然等价于 (C_{wid}^kC_{hig}^kk!) ,表示选出 k 种高度和下标并配对,且点之间有标号

然后我们在笛卡尔树上DP,设 (f_{i,j}) 表示节点 (i) 所代表的子树内选了 (j) 个点的方案,转移方程如下:

[egin{cases} f_{u,i}=f_{u,i}+f_{v,j}*f_{u,i-j} (子树向父亲贡献) \ f_{u,i}=f_{u,i}+f_{u,i-j}*C_{siz-(i-j)}^jC_{hig}^j*j!(选择自己代表的矩形) end{cases} ]

代码:

#include<bits/stdc++.h>
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
    typedef long long ll;
    inl ll read()
    {
        ll x=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
        return x*f;
    }

    const ll mod = 1e9+7;
    const ll maxn = 505;
    const ll maxm = 1e6+5;
    ll n,k,top;
    ll ch[maxn][2],a[maxn],st[maxn],f[maxn][maxn],fac[maxm],inv[maxm],wid[maxn],siz[maxn];

    void init()
    {
        fac[0]=fac[1]=1;
        inv[0]=inv[1]=1;
        for(reg ll i=2;i<=1000000;i++) fac[i]=fac[i-1]*i%mod,inv[i]=inv[mod%i]*(mod-mod/i)%mod;
        for(reg ll i=2;i<=1000000;i++) inv[i]=inv[i]*inv[i-1]%mod;
    }

    ll C(ll n,ll m)
    {
        if(n<m) return 0;
        return fac[n]*inv[n-m]%mod*inv[m]%mod;
    }

    void dfs(ll u,ll hig)
    {
        hig=a[u]-hig;
        f[u][0]=1;siz[u]=1;
        for(reg ll t=0;t<=1;t++)
        {
            if(!ch[u][t]) continue;
            dfs(ch[u][t],a[u]);
            siz[u]+=siz[ch[u][t]];
            for(reg ll i=min(siz[u],k);i>=0;i--) 
                for(reg ll j=1;j<=min(siz[ch[u][t]],i);j++) 
                    f[u][i]=(f[u][i]+f[u][i-j]*f[ch[u][t]][j]%mod)%mod;
        }
        for(reg ll i=min(siz[u],k);i>=0;i--)
            for(reg ll j=1;j<=min(hig,i);j++)
                f[u][i]=(f[u][i]+f[u][i-j]*C(siz[u]-(i-j),j)%mod*C(hig,j)%mod*fac[j]%mod)%mod;
    }

    void work()
    {
        init();
        n=read();k=read();
        for(reg ll i=1;i<=n;i++) a[i]=read();
        for(reg ll i=1;i<=n;i++)
        {
            ll j=top;
            while(j&&a[st[j]]>a[i]) j--;
            if(j) ch[st[j]][1]=i;
            if(j<top) ch[i][0]=st[j+1];
            top=j;st[++top]=i;
        }
        dfs(st[1],0);
        printf("%lld
",f[st[1]][k]);
    }
}

int main()
{
    zzc::work();
    return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14485171.html