題目鏈接:E. Another Filling the Grid
題目大意:在給定n∗n方格中放數字,數字範圍是[1,k],問在每一行每一列的最小數字是1(每一行每一列至少有一個1)的方案數有多少種?
題目思路:雖然這題數據範圍小,但是比賽時想到結束都沒想出來要怎麼解。
(神仙網友的題解,趕緊學習一波)
對於給定大小的有限解,考慮容斥!
於是就有:可行方案數= 總方案數 -(一行不合法 + 一列不合法)+ (兩行不合法 + 兩列不合法)-(三行不合法 + 三列不合法)+…
寫成表達式的形式:
∑i=0n∑j=0n(−1)i+j∗Cni∗Cnj∗kn2−n∗(i+j)+i∗j∗(k−1)n∗(i+j)−i∗j
上面表達式中:(k−1)n∗(i+j)−i∗j表示強制i行j列的格子不能填1,kn2−n∗(i+j)+i∗j表示剩下沒有限制的格子可以隨便填數,我們還可以對錶達式進一步化簡:
∑i=0n∑j=0n(−1)i+j∗Cni∗Cnj∗kn2−n∗(i+j)+i∗j∗(k−1)n∗(i+j)−i∗j
=∑i=0n(−1)i∗Cni∗∑j=0n(−1)j∗Cnj∗kn2−n∗(i+j)+i∗j∗(k−1)n∗(i+j)−i∗j
=∑i=0n(−1)i∗Cni∗∑j=0n(−1)j∗Cnj∗k(n−i)∗(n−j)∗(k−1)(n−i)∗j∗(k−1)n∗i
因爲:
∑j=0n(−1)j∗Cnj∗k(n−i)∗(n−j)∗(k−1)(n−i)∗j
=(kn−i−(k−1)n−i)n(證明佔坑)
所以原式:
=∑i=0n(−1)i∗Cni∗(kn−i−(k−1)n−i)n∗(k−1)n∗i
=∑i=0n(−1)i∗Cni∗((k−1)i∗(kn−i−(k−1)n−i))n
=∑i=0n(−1)i∗Cni∗(kn−i∗(k−1)i−(k−1)n)n
然後就可以快樂地敲代碼了。
複雜度:O(n∗log(n))
AC代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 1e3 + 10;
int n,k;
ll qpow(ll x,ll y){
ll ans=1;
while(y>0){
if(y&1){
ans=ans*x%mod;
}
x=(ll)x*x%mod;
y>>=1;
}
return ans;
}
ll f[maxn];
ll inv[maxn];
void calfact(){
f[0]=1;
for(int i=1;i<maxn;i++){
f[i]=(f[i-1]*i)%mod;
}
inv[maxn-1]=qpow(f[maxn-1],mod-2);
for(int i=maxn-2;~i;i--){
inv[i]=inv[i+1]*(i+1)%mod;
}
}
ll C(int n,int m){
return f[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
cin>>n>>k;
ll ans=0;
ll flag;
calfact();
for(int i = 0 ; i <= n; i ++){
if(i%2==0){
flag=1;
}
else{
flag=-1;
}
ll tmp = ( (ll)qpow( k , n - i ) * qpow( k - 1, i ) % mod - qpow( k - 1, n) + mod ) % mod;
tmp = qpow(tmp, n) % mod;
ans=(ans + (ll)flag * C( n , i ) * tmp % mod + mod) % mod;
}
cout<<ans<<endl;
return 0;
}
參考:https://www.cnblogs.com/LLTYYC/p/11612535.html