Codeforces Round #589 (Div. 2)E. Another Filling the Grid【容斥】

題目鏈接:E. Another Filling the Grid

題目大意:在給定nnn*n方格中放數字,數字範圍是[1,k][1,k],問在每一行每一列的最小數字是1(每一行每一列至少有一個1)的方案數有多少種?

題目思路:雖然這題數據範圍小,但是比賽時想到結束都沒想出來要怎麼解。
(神仙網友的題解,趕緊學習一波)

對於給定大小的有限解,考慮容斥!

於是就有:可行方案數= 總方案數 -(一行不合法 + 一列不合法)+ (兩行不合法 + 兩列不合法)-(三行不合法 + 三列不合法)+\dots

寫成表達式的形式:

i=0nj=0n(1)i+jCniCnjkn2n(i+j)+ij(k1)n(i+j)ij\sum_{i=0}^n\sum_{j=0}^n(-1)^{i+j}*C_{n}^{i}*C_n^j*k^{n^2-n*(i+j)+i*j}*(k-1)^{n*(i+j)-i*j}

上面表達式中:(k1)n(i+j)ij(k-1)^{n*(i+j)-i*j}表示強制iijj列的格子不能填1,kn2n(i+j)+ijk^{n^2-n*(i+j)+i*j}表示剩下沒有限制的格子可以隨便填數,我們還可以對錶達式進一步化簡:

i=0nj=0n(1)i+jCniCnjkn2n(i+j)+ij(k1)n(i+j)ij\sum_{i=0}^n\sum_{j=0}^n(-1)^{i+j}*C_{n}^{i}*C_n^j*k^{n^2-n*(i+j)+i*j}*(k-1)^{n*(i+j)-i*j}

=i=0n(1)iCnij=0n(1)jCnjkn2n(i+j)+ij(k1)n(i+j)ij=\sum_{i=0}^n(-1)^{i}*C_{n}^{i}*\sum_{j=0}^n(-1)^j*C_n^j*k^{n^2-n*(i+j)+i*j}*(k-1)^{n*(i+j)-i*j}

=i=0n(1)iCnij=0n(1)jCnjk(ni)(nj)(k1)(ni)j(k1)ni=\sum_{i=0}^n(-1)^{i}*C_{n}^{i}*\sum_{j=0}^n(-1)^j*C_n^j*k^{(n-i)*(n-j)}*(k-1)^{(n-i)*j}*(k-1)^{n*i}

因爲:

j=0n(1)jCnjk(ni)(nj)(k1)(ni)j\sum_{j=0}^n(-1)^j*C_n^j*k^{(n-i)*(n-j)}*(k-1)^{(n-i)*j}

=(kni(k1)ni)n=(k^{n-i}-(k-1)^{n-i})^n(證明佔坑)

所以原式:

=i=0n(1)iCni(kni(k1)ni)n(k1)ni=\sum_{i=0}^n(-1)^{i}*C_{n}^{i}*(k^{n-i}-(k-1)^{n-i})^n*(k-1)^{n*i}

=i=0n(1)iCni((k1)i(kni(k1)ni))n=\sum_{i=0}^n(-1)^{i}*C_{n}^{i}*((k-1)^{i}*(k^{n-i}-(k-1)^{n-i}))^n

=i=0n(1)iCni(kni(k1)i(k1)n)n=\sum_{i=0}^n(-1)^{i}*C_{n}^{i}*(k^{n-i}*(k-1)^{i}-(k-1)^{n})^n

然後就可以快樂地敲代碼了。

複雜度:O(nlog(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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章