Codeforces Round #589 (Div. 2) E.Another Filling the Grid
题目链接
大意:给你一个n*n的矩阵和k,让你往每个单元格填1-k的数,使得每行每列的最小值都是1.问有多少种构造方法。
思路:考虑 dp[i][j]意为,已经满足i行最小值都是1,但是有j列的最小值不是1的方案。
初始 dp[0][n]=1
然后每次转移的话就是 dp[i][j]显然是从 dp[i−1][x],x≤j转移而来的。
分两种情况:
1. x=j 那么第i行也有j个必然不是1,剩余n-j个格子必然存在1,那么总共的方案就是 dp[i−1][j]∗(k−1)j∗(kn−j−(k−1)n−j)
2. x>j,这时候我们枚举x来进行转移,假设当前为y,那么此时我们必然从上一个状态下选 y−j个放1, n−y个随便放, j个不放1.直接累计即可。
答案输出 dp[n][0]
注意中间的幂次方要预处理一下。
细节见代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define LL long long
#define SZ(X) X.size()
#define pii pair<int,int>
#define ALL(X) X.begin(),X.end()
using namespace std;
LL gcd(LL a, LL b) {return b ? gcd(b, a % b) : a;}
LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
LL powmod(LL a, LL b, LL MOD) {LL ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;}
const int N = 2e3 + 11;
const LL mod=1e9+7;
LL dp[N][N],f[N],q[N];
LL n,k;
LL get(int a,int b){
return f[a]*q[b]%mod*q[a-b]%mod;
}
LL P[N],Q[N];
int main() {
ios::sync_with_stdio(false);
f[0]=1;
for(int i=1;i<N;i++)f[i]=f[i-1]*i%mod;
for(int i=0;i<N;i++)q[i]=powmod(f[i],mod-2,mod);
P[0]=Q[0]=1;cin>>n>>k;
for(int i=1;i<N;i++)P[i]=P[i-1]*k%mod,Q[i]=Q[i-1]*(k-1)%mod;
//dp[i][j]表示已经选了i行,还有j列没选
dp[0][n]=1;
for(int i=1;i<=n;i++){
for(int j=n;j>=0;j--){
dp[i][j]=dp[i-1][j]*(P[n-j]-Q[n-j]+mod)%mod*Q[j]%mod;
for(int K=j+1;K<=n;K++){
dp[i][j]=dp[i][j]+dp[i-1][K]*get(K,K-j)%mod*Q[j]%mod*P[n-K]%mod;
dp[i][j]%=mod;
}
}
}
cout<<dp[n][0]<<endl;
return 0;
}