Description
对一个n*m的矩形染k种颜色,并且满足着色对于任一条竖直的线,把矩形分成不为空的两部分,每部分中的不同颜色的种数要相同。(竖直的线即整列分割)
n,m<=1000,k<=10^6
Solution
被liuzhenyu强行推了这道题(以及其他很多题)
以后有时间可以写一个被强推的题的合集~
花了一节数学课啥都没推出来,然后课间过了5min立马发现自己zz了~
我们考虑一条线,设左右两边的颜色数量都为c,那么我们将这条线右移,左边加上了这一列的颜色钟数,右边减去了这一列的颜色种数,然后我们仍然要让左右保持相等,那么久表示,我们这一列的颜色必须填左边和右边都出现过的。
推广一下,发现除了最左边和最右边那两列以外其他的列都满足这个性质。
于是我们只用考虑左右两列就好了。
首先,很显然要枚举一个i,表示左右两边重复的颜色种数,那么对于所有在中间的方格都可以填这i种颜色中的任意一种。所以我们有
Cik∗i(m−2)∗n
种方案。
接下来枚举一个j,表示左右两边各有另外j种不同的颜色,因为我们也要保证左右两边的颜色种数相同。
我们设cnt[i]表示用i种颜色填满这n个格子且每种颜色都会出现的方案数。
那么我们有
cnt[i+j]2∗Cjk−i∗Cjk−i−j
种方案。
因为左右两边是独立的,但要保证两边的这j种颜色都不相同。
最后我们考虑如何计算cnt。
显然可以容斥。
cnt[i]=∑j=1iCji∗in∗(−1)i−j
O(N^2)预处理cnt,O(N^2)计算答案。
总复杂度O(N^2)
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e6,mo=1e9+7;
int mi(int x,int y) {
int z=1;
for(;y;y/=2,x=(ll)x*x%mo) if (y&1) z=(ll)z*x%mo;
return z;
}
int n,m,k,ans,fact[N+5],inv[N+5],cnt[N];
int c(int m,int n) {
if (m<0||n<0||m<n) return 0;
return (ll)fact[m]*inv[n]%mo*inv[m-n]%mo;
}
int main() {
scanf("%d%d%d",&n,&m,&k);
fact[0]=inv[0]=1;
fo(i,1,N) fact[i]=(ll)fact[i-1]*i%mo;
inv[N]=mi(fact[N],mo-2);
fd(i,N-1,1) inv[i]=(ll)inv[i+1]*(i+1)%mo;
fo(i,1,n)
fo(j,1,i)
if ((j-i)&1) (cnt[i]+=mo-(ll)mi(j,n)*c(i,j)%mo)%=mo;
else (cnt[i]+=(ll)mi(j,n)*c(i,j)%mo)%=mo;
fo(i,0,n) fo(j,0,n-i) (ans+=(ll)c(k,i)*c(k-i,j)%mo*
c(k-i-j,j)%mo*cnt[i+j]%mo*cnt[i+j]%mo*mi(i,(m-2)*n)%mo)%=mo;
printf("%d\n",ans);
}