题意:
给出
N
,
K
(
1
≤
N
≤
250
,
1
≤
K
≤
1
0
9
)
N,K(1\leq N \leq250,1\leq K \leq 10^9)
N,K(1≤N≤250,1≤K≤109),求
N
∗
N
N*N
N∗N矩阵每行每列至少有一个
1
1
1的方案数(每行至少有一个1,每列至少也有一个1)
题解:
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示在
i
∗
N
i*N
i∗N这个子矩阵中已经有
j
j
j列是至少有一个1的方案数,现在进行状态转移:
已经有
j
j
j列有1,那么还有
N
−
j
N-j
N−j列没有1,在
N
−
j
N-j
N−j列中选出
k
k
k列,把这
k
k
k列选中为1,其他
N
−
j
−
k
N-j-k
N−j−k列的方案数就是
(
K
−
1
)
N
−
j
−
k
(K-1)^{N-j-k}
(K−1)N−j−k
这里还有要注意的一点,如果
k
=
0
k=0
k=0?那么这行就有可能用部分方案数是零个1,这是不符合条件的,因此需要减掉
总结:
d
p
[
i
+
1
]
[
j
+
k
]
+
=
d
p
[
i
]
[
k
]
∗
C
N
−
j
k
∗
K
j
∗
(
K
−
1
)
N
−
j
−
k
dp[i+1][j+k]+=dp[i][k]*C_{N-j}^k*K^j*(K-1)^{N-j-k}
dp[i+1][j+k]+=dp[i][k]∗CN−jk∗Kj∗(K−1)N−j−k
当
k
=
0
k=0
k=0时需要处理:
d
p
[
i
+
1
]
[
j
+
k
]
−
=
d
p
[
i
]
[
j
]
∗
(
K
−
1
)
N
dp[i+1][j+k]-=dp[i][j]*(K-1)^N
dp[i+1][j+k]−=dp[i][j]∗(K−1)N
整合下两个转移方程
d
p
[
i
+
1
]
[
j
+
k
]
+
=
d
p
[
i
]
[
j
]
∗
(
C
N
−
j
k
∗
K
j
∗
(
K
−
1
)
N
−
j
−
k
−
(
k
=
=
0
)
∗
(
K
−
1
)
N
)
dp[i+1][j+k]+=dp[i][j]*(C_{N-j}^k*K^j*(K-1)^{N-j-k}-(k==0)*(K-1)^N)
dp[i+1][j+k]+=dp[i][j]∗(CN−jk∗Kj∗(K−1)N−j−k−(k==0)∗(K−1)N)
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int maxn=255;
ll fac[maxn],inv[maxn],power_k[maxn],power_k_1[maxn],dp[maxn][maxn];
ll fast(ll x,ll y=mod-2){
ll ans=1;
while(y){
if(y&1)ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
void init(int n,int k){
fac[0]=1;power_k[0]=power_k_1[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*i%mod;
power_k[i]=power_k[i-1]*k%mod;
power_k_1[i]=power_k_1[i-1]*(k-1)%mod;
}
inv[n]=fast(fac[n]);
for(int i=n-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(int n,int m){
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
int n,K;
scanf("%d%d",&n,&K);
init(n,K);
dp[0][0]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=n;j++){
if(dp[i][j]==0)continue;
for(int k=0;k<=n-j;k++){
dp[i+1][j+k]=(dp[i+1][j+k]+dp[i][j]*(C(n-j,k)*power_k[j]%mod*power_k_1[n-j-k]%mod-(k==0)*power_k_1[n]+mod)%mod)%mod;
}
}
}
cout<<dp[n][n]<<endl;
return 0;
}