[Codeforces 1228E]Another Filling the Grid (排列組合+容斥原理)
題面
一個\(n \times n\)的格子,每個格子里可以填\([1,k]\)內的整數。要保證每行每列的格子上的數最小值為1,有多少種方案
\(n \leq 250,k \leq 10^9\)
分析
這題有\(O(n^3)\)的dp做法,但個人感覺不如\(O(n^2 \log n)\)直接用數學方法求更好理解。
考慮容斥原理,枚舉至少有\(i\)行最小值>1,有\(j\)行最小值>1,那答案就是\(\sum_{i=0}^n \sum_{j=0}^n (-1)^{i+j} C_n^i C_n^j (有至少i行最小值>1,有至少j行最小值>1的方案數)\)。其中\(C_n^i,C_n^j\)表示從n行中選出i行,n列中選出j列。容斥是因為i行j列的情況可能包含行數<i,列數<j的情況。
然后某一行的最小值>1,那這行里的所有數都>1。因此只要求出哪些格子里的數>1即可。顯然i行j列包含的格子數為\(ni+nj-ij\).這些格子的填法有\({(k-1)}^{ni+nj-ij}\)種(不能填1),其余格子的填法為\(k^{n^2-ni-nj+ij}\)
因此答案為
枚舉i,j,然后快速冪求逆元,時間復雜度為\(O(n^2 \log n)\)。可以通過。
注意到第二個sigma類似二項式定理,分離出質數\(j\)和\(n-j\)
\({(k-1)}^{ni+nj-ij} k^{n^2-ni-nj+ij}=k^{(n-j)(n-i)}(k-1)^{(n-j)i}(k-1)^{j \times n}=[k^{n-i}(k-1)^i]^{n-j} {((k-1)^n)}^{j}\)
因此答案為
這樣就可以做到\(O(n\log n)\)了
代碼
//O(n^2logn)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 250
#define mod 1000000007
using namespace std;
typedef long long ll;
inline ll fast_pow(ll x,ll k){
ll ans=1;
while(k){
if(k&1) ans=ans*x%mod;
x=x*x%mod;
k>>=1;
}
return ans;
}
ll inv(ll x){
return fast_pow(x,mod-2);
}
ll fact[maxn+5],invfact[maxn+5];
void ini(int n){
fact[0]=1;
for(int i=1;i<=n;i++) fact[i]=fact[i-1]*i%mod;
invfact[n]=inv(fact[n]);
for(int i=n-1;i>=0;i--) invfact[i]=invfact[i+1]*(i+1)%mod;
}
inline ll C(int n,int m){
return fact[n]*invfact[n-m]%mod*invfact[m]%mod;
}
int n,k;
int main(){
scanf("%d %d",&n,&k);
ini(n);
ll ans=0;
for(int i=0;i<=n;i++){//共i行不符合條件
for(int j=0;j<=n;j++){//共j列不符合條件
ll cnt=i*n+j*n-i*j;//不符合條件行和列的格子,這些格子的值>1
ans+=fast_pow(-1,i+j)*C(n,i)%mod*C(n,j)%mod*fast_pow(k-1,cnt)%mod*fast_pow(k,n*n-cnt)%mod;
ans=(ans+mod)%mod;
}
}
printf("%I64d\n",ans);
}