Description
给你N行M列的棋盘,让你放棋子,每行每列至少有1枚棋子,棋子有c种颜色,要求每种颜色至少1枚,求方案数(旋转,翻转算不同方案)。
Solution
正难则反,考虑设
i,j
表示
i
行
显然可以容斥。
剩下
(n−i)(m−j)
个位置。
设
t=(n−i)(m−j)
设第
c+1
种颜色表示不放。
那么这一部分的方案数就是
S(t,c+1)∗(c+1)!+S(t,c)∗c!
S
表示第二类斯特林数。
什么意思呢?
详解见http://blog.csdn.net/hzj1054689699/article/details/54644649,此处不再赘述。
因为第c+1种颜色是可有可没有的(不放),并且每种颜色是有差别的。
有的就是
S(t,c+1)∗(c+1)!
,没有的就是
S(t,c)∗c!
这个东西可以O(NMC)预处理。
然后看选的方案数,显然是
CinCjm
因为是至少i,j行,所以还要容斥。
总的来就是
Ans=∑i=1n∑i=1n(−1)i+jCinCjm(S(t,c+1)∗(c+1)!+S(t,c)∗c!)
这是NM的
总的复杂度是O(NMC)
Code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 405
#define mo 1000000007
#define LL long long
using namespace std;
LL js[N*N],n,m,c,ny[N];
int f[N*N][N];
LL ksm(LL k,LL n)
{
if(n==0) return 1;
LL s=ksm(k,n/2);
return(n%2)?s*s%mo*k%mo:s*s%mo;
}
LL C(LL m,LL n)
{
return js[n]*ny[m]%mo*ny[n-m]%mo;
}
int main()
{
freopen("chess.in","r",stdin);
cin>>n>>m>>c;
f[0][0]=1;
js[0]=ny[0]=1;
fo(i,1,max(max(n,m),c+1)) js[i]=(js[i-1]*(LL)i)%mo,ny[i]=ksm(js[i],mo-2);
fo(i,1,n*m)
fo(j,1,c+1) f[i][j]=((LL)f[i-1][j]*(LL)j%mo+(LL)f[i-1][j-1])%mo;
LL ans=0;
fo(i,0,n)
{
fo(j,0,m)
{
int v=((i+j)%2)?-1:1;
ans=(ans+v*C(i,n)*C(j,m)%mo*((LL)f[(n-i)*(m-j)][c+1]*js[c+1]%mo+(LL)f[(n-i)*(m-j)][c]*js[c]%mo))%mo;
}
}
cout<<(ans+mo)%mo;
}