传送门

看到 $n=250$ 显然考虑 $n^3$ 的 $dp$

设 $f[i][j]$ 表示填完前 $i$ 行,目前有 $j$ 列的最小值是 $1$ 的合法方案数

那么对于 $f[i][j]$ ,枚举 $f[i-1][k]$ ,有 $f[i][j]=\sum_{k=0}^{j}\binom{n-k}{j-k}f[i-1][k](m-1)^{n-j}m^k$

这里 $m$ 就是题目的 $k$

$\binom{n-k}{j-k}$ 是因为多出来的 $j-k$ 列 $1$ 可以任选

$(m-1)^{n-j}$ 是保证没有 $1$ 的列不能填 $1$ ,只有 $m-1$ 种填的数

$m^k$ 是那些原本有保证为 $1$ 的列怎么填都行

当然剩下的那 $j-k$ 个位置显然都是 $1$ ,方案数只有 $1$

然后这样就可以做到 $n^3 \log n$ 然后发现竟然 $T$ 了,所以预处理一下 $k \in [0,n],m^k$ 和 $k \in [0,n],(m-1)^k$ 即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int mo=1e9+7,N=507;
inline int fk(int x) { return x>=mo ? x-mo : x; }
int n,m;
int C[N][N],f[N][N];
int mi[N],mi_1[N];
int main()
{
    n=read(),m=read();
    for(int i=0;i<=n;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
            C[i][j]=fk(C[i-1][j]+C[i-1][j-1]);
    }
    mi[0]=mi_1[0]=1;
    for(int i=1;i<=n;i++)
    {
        mi[i]=1ll*mi[i-1]*m%mo;
        mi_1[i]=1ll*mi_1[i-1]*(m-1)%mo;
    }
    for(int j=1;j<=n;j++) f[1][j]=1ll*C[n][j]*mi_1[n-j]%mo;
    for(int i=2;i<=n;i++)
        for(int j=0;j<=n;j++)
        {
            for(int k=0;k<=j;k++)
            {
                int x=1ll*f[i-1][k]*C[n-k][j-k]%mo;
                int y=1ll*mi_1[n-j]*mi[k]%mo;
                f[i][j]=fk(f[i][j]+1ll*x*y%mo);
                if(j==k) f[i][j]=fk(f[i][j]-1ll*mi_1[n]*f[i-1][k]%mo+mo);
            }
        }
    printf("%d\n",f[n][n]);
    return 0;
}
正常的做法

相关文章: