题目大意:有一个 n ∗ n n * n n∗n 的矩阵,每个格子可以填 1 − k 1 - k 1−k 这 k 种值,要使得每一行每一列都有 1,填数的方案数是多少。
有一个 n log n n \log n nlogn 的做法,先保证每一列有 1,每一列都有 1 的方案数很好求,但每一列都 1 并不能保证每一行有 1。
可以用总的方案数减去有些行没有 1 的个数,这个过程可以用容斥原理。
设有
i
i
i 行 没有 1,而每一列都有 1的方案数为
f
(
i
)
f(i)
f(i)
每一列有 1 的方案数可以用减法
f
(
i
)
=
C
(
n
,
i
)
∗
(
(
k
−
1
)
i
∗
k
n
−
i
−
(
k
−
1
)
n
)
n
f(i) = C(n,i) *((k - 1)^i *k^{n-i} -(k-1)^{n})^n
f(i)=C(n,i)∗((k−1)i∗kn−i−(k−1)n)n
代码:
#include<iostream>
using namespace std;
const int mod = 1e9 + 7;
#include<stdio.h>
#include<string.h>
typedef long long ll;
const int maxn = 1e3 + 10;
int n,k;
ll fact[maxn],ifact[maxn];
ll fpow(ll a,ll b) {
ll r = 1;
while(b) {
if(b & 1) r = r * a % mod;
a = a * a % mod;
b >>= 1;
}
return r;
}
ll C(int x,int y) {
if(y > x || y < 0) return 0;
return fact[x] * ifact[y] % mod * ifact[x - y] % mod;
}
int main() {
fact[0] = 1;
for(int i = 1; i < maxn; i++)
fact[i] = fact[i - 1] * i % mod;
ifact[1001] = fpow(fact[1001],mod - 2);
for(int i = 1000; i >= 0; i--)
ifact[i] = ifact[i + 1] * (i + 1) % mod;
scanf("%d%d",&n,&k);
ll res = 0,ans = 0;
for(int i = 0; i <= n; i++) {
ll tmp = (fpow(k,n - i) * fpow(k - 1,i) % mod - fpow(k - 1,n) + mod) % mod;
tmp = fpow(tmp,n);
tmp = tmp * C(n,i) % mod;
if(i & 1) tmp = tmp * -1;
ans = (ans + tmp + mod) % mod;
}
printf("%lld\n",ans);
return 0;
}