CF1228E Another Filling the Grid【容斥】
阿新 • • 發佈:2020-12-04
題目解析
剛開始的思路:我先欽定好,給每行每列一個家\(1\),其它隨便選,一共\(n!k^{(n^2-n)}\)
但是會有重複,考慮去重。我們還是先給每行每列一個家,但是後面那個不能隨便選,我們列舉一些行,一些列,然後讓它們隨便選,剩下的不能隨便選,發現這樣也有重複,所以考慮容斥。
看了題解之後發現一個更自然的想法。
我們可以用總方案數減去不合法的方案數。現在考慮不合法的方案長啥樣。還是按照欽定多少行/多少列有無\(1\)的思路來,我們先減去“有一行/列(第\(i\)行/列)沒有\(1\),其它隨便選”,而這個方案和“有兩行/兩列/一行一列沒有\(1\),其它隨便選”有重疊,所以把這樣的方案加回來,而上個方案數也有重疊,我們又多加了一次“有三行/三列/兩行一列/一行兩列沒有\(1\)
我們把總方案數看成“欽定\(0\)行\(0\)列沒有\(1\),其它隨便選”,於是可以合併在容斥式子裡:
\(ans=\sum_{i=0}^n\sum_{j=0}^nC_n^iC_n^j(k-1)^{n(i+j)-ij}k^{n^2-ni-nj+ij}\)
先無序選出\(i\)行\(j\)列,這\(i\)行\(j\)列上的數除了\(1\)以外隨便選,剩下的格子可以隨便選。
其實還可以用二項式定理優化,還有\(dp\)寫法,但是咕咕咕~
►Code View
#include<cstdio> #include<algorithm> #include<queue> #include<set> using namespace std; #define N 260 #define MOD 1000000007 #define INF 0x3f3f3f3f3f3f3f3f #define LL long long LL rd() { LL x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();} return f*x; } int n; LL k; LL fac[N],inv[N]; LL ksm(LL a,LL b) { LL res=1ll; while(b) { if(b&1) res=res*a%MOD; a=a*a%MOD; b>>=1; } return res; } void Init() { fac[0]=1,inv[0]=1; for(int i=1;i<=N-5;i++) fac[i]=fac[i-1]*i%MOD; inv[N-5]=ksm(fac[N-5],MOD-2); for(int i=N-6;i>=1;i--) inv[i]=inv[i+1]*(i+1)%MOD; } LL C(int a,int b) { return fac[a]*inv[a-b]%MOD*inv[b]%MOD; } int main() { n=rd(),k=rd(); Init(); LL ans=0; for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) { LL res=C(n,i)*C(n,j)%MOD; res=res*ksm(k-1,n*(i+j)-i*j)%MOD*ksm(k,(n-i)*(n-j))%MOD; if((i+j)&1) ans=(ans-res+MOD)%MOD; else ans=(ans+res)%MOD;//忘了寫else可還行 調了20min } printf("%lld\n",ans); return 0; }