题目链接: https://codeforces.com/contest/1228/problem/E
题意:
你现在有个 n ∗ n n*n n∗n 的矩阵,现在要你给每个格子填上一个数字 x ( x ∈ [ 1 , k ] ) x(x\in[1,k]) x(x∈[1,k]) ,并且要求每一行以及每一列的最小值是 1 1 1 ,问你有多少种填数字的方法。
做法:
只会 O ( n 3 ) O(n^3) O(n3) 的做法,果然还是太菜了。。 O ( n 2 ) O(n^2) O(n2) 的做法表示没弄懂…先写下三次方的做法…
d p [ i ] [ j ] dp[i][j] dp[i][j] 合法代表枚举到第 i i i 列时,仍有 j j j 行还没有符合条件的方法种数。
d p [ i ] [ j ] dp[i][j] dp[i][j] 可以从两种情况转移过来,① d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 表示没有加进新的边,② d p [ i ] [ x ] ( x ∈ [ j + 1 , n ] ) dp[i][x](x\in[j+1,n]) dp[i][x](x∈[j+1,n]) ,表示有在新的行上增加 1 1 1。
为什么要这样把两种情况分开呢,因为如果有在新的行上增加 1 1 1 的话,原来已经满足条件的行就无所谓是不是一定有 1 1 1 了,而如果没有新增加的话,就必须满足原来的行中至少有一行有 1 1 1 ,这样才能满足在这一列的时候列合法的情况。
① d p [ i ] [ j ] + = d p [ i − 1 ] [ j ] ∗ ( k − 1 ) j ( k n − j − ( k − 1 ) n − j ) dp[i][j]+=dp[i-1][j]*(k-1)^j(k^{n-j}-(k-1)^{n-j}) dp[i][j]+=dp[i−1][j]∗(k−1)j(kn−j−(k−1)n−j) 表示,未满足的原来的 j j j 行中可以任意取除了 1 1 1 以外的值,而满足条件的 n − j n-j n−j 行除了不能全部取非 1 1 1 值以外任意取。
② d p [ i ] [ j ] + = ∑ p = j + 1 n d p [ i − 1 ] [ p ] ∗ C p p − j ∗ ( k − 1 ) j ∗ k n − p dp[i][j]+=\sum_{p=j+1}^{n} dp[i-1][p]*C_{p}^{p-j}*(k-1)^j*k^{n-p} dp[i][j]+=∑p=j+1ndp[i−1][p]∗Cpp−j∗(k−1)j∗kn−p 表示,假设我们从原来的值为 p p p 时转移过来,要从中选 p − j p-j p−j 个填上 1 1 1 , j j j 行依旧是可以取任意非 1 1 1 的值,除了这 p p p 行之外的行可以任意取。(注意这里是可以任意取,原因上面说过了)。
最后的 d p [ n ] [ 0 ] dp[n][0] dp[n][0] 就是我们要的答案。
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=255;
ll dp[maxn][maxn],k,C[maxn][maxn];
ll n,qk[maxn],qk1[maxn];
ll quick(ll a,ll b){
ll ans=1;
while(b){
ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
void init(){
rep(i,0,251) C[i][0]=1;
rep(i,1,251){
rep(j,1,i){
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
qk[0]=qk1[0]=1;
rep(i,1,251) {
qk[i]=qk[i-1]*k%mod,qk1[i]=qk1[i-1]*(k-1ll)%mod;
}
}
int main(){
scanf("%lld%lld",&n,&k);
init();
dp[0][n]=1;
rep(i,1,n){
rep(j,0,n){
dp[i][j]=dp[i-1][j]*qk1[j]%mod*((qk[n-j]-qk1[n-j]+mod)%mod)%mod;
rep(p,j+1,n){
dp[i][j]=(dp[i][j]+dp[i-1][p]*C[p][p-j]%mod*qk1[j]%mod*qk[n-p]%mod)%mod;
}
}
}
printf("%lld\n",dp[n][0]);
return 0;
}