SPOJ3734 PERIODNI - Periodni

逆元送自闭现场

Description

link

题意简述:给定一个 \(N\) 列的表格,每列的高度各不相同,但底部对齐

然后向表格中填入 \(K\) 个相同的数,填写时要求不能有两个数在同一列,或同一行(如果表格断开没事)

求方案数

\(N \le 500\)

Solution

\[Begin\]

计数类 \(dp\)吧,这个显然

我们看到这个是一个直方图,而且形状不规则,就考虑到建一棵笛卡尔树

然后把题目转成在矩形中选点(笛卡尔树把直方图变成一个一个的矩形,然后就是放点了)

这里有个结论:\(n \times m\) 的矩形中我们放 \(k\) 点,方案数为:\(C^{k}_n \times C^k_m \times k \space !\)

每个点都占一行和一列,所以两个组合数的乘积就是一个组合,排列数直接乘阶乘就好

然后是笛卡尔树上的 \(dp\), 这里转移就考虑枚举在每一岔上放几个点就行了,最后加一加,乘一乘

\[Q.A.D\]

\(P.S.\) 博主知道是\(QED\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
    inline int read()
    {
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int N=5010,mod=1e9+7,M=1e6+10;
    int n,k,top,rt;
    int ls[N],rs[N],st[N];
    int f[N][N],a[N],sz[N],fac[M],inv[M];
    inline int ksm(int x,int y)
    {
        int res=1; for(;y;y>>=1,(x*=x)%=mod) if(y&1) (res*=x)%=mod;
        return res;
    }
    inline void prework()
    {
        fac[0]=fac[1]=inv[0]=inv[1]=1;
        for(int i=2;i<N;++i)
        {
            fac[i]=(fac[i-1]*i)%mod;
            inv[i]=ksm(fac[i],mod-2);
        } 
        for(int i=1;i<=n;++i)
        {
            while(top&&a[st[top]]>a[i]) ls[i]=st[top--];
            if(top) rs[st[top]]=i;
            st[++top]=i;
        }
        rt=st[1];
        return ;
    }
    inline int C(int n,int m){return n<m? 0:fac[n]*inv[m]%mod*inv[n-m]%mod;}
    void dfs(int x,int val)
    {
        f[x][0]=sz[x]=1;
        int h=a[x]-val;
        if(ls[x])
        {
            dfs(ls[x],a[x]),sz[x]+=sz[ls[x]];
            for(int i=min(sz[x],k);i>=0;--i)
            {
                for(int j=1;j<=min(sz[ls[x]],i);++j)
                {
                    f[x][i]=(f[x][i]+f[ls[x]][j]*f[x][i-j]%mod)%mod;
                }
            }  
        }
        if(rs[x])
        {
            dfs(rs[x],a[x]),sz[x]+=sz[rs[x]];
            for(int i=min(sz[x],k);i>=0;--i)
            {
                for(int j=1;j<=min(sz[rs[x]],i);++j)
                {
                    f[x][i]=(f[x][i]+f[rs[x]][j]*f[x][i-j]%mod)%mod;
                }
            }         
        } 
        for(int i=min(sz[x],k);i>=0;--i)
        {
            for(int j=1;j<=min(h,i);++j)
            {
                f[x][i]=(f[x][i]+f[x][i-j]*fac[j]%mod*C(h,j)%mod*C(sz[x]-(i-j),j)%mod)%mod;
            }
        } return ;
    }
    int main()
    {
        n=read(); k=read();
        for(int i=1;i<=n;++i) a[i]=read();
        prework();
        dfs(rt,0);
        printf("%lld",f[rt][k]);
        return 0;
    }
}
signed main(){return yspm::main();}

猜你喜欢

转载自www.cnblogs.com/yspm/p/12384937.html