[51nod1604]对称的方格颜色

Description

对一个n*m的矩形染k种颜色,并且满足着色对于任一条竖直的线,把矩形分成不为空的两部分,每部分中的不同颜色的种数要相同。(竖直的线即整列分割)
n,m<=1000,k<=10^6

Solution

被liuzhenyu强行推了这道题(以及其他很多题)
以后有时间可以写一个被强推的题的合集~

花了一节数学课啥都没推出来,然后课间过了5min立马发现自己zz了~
我们考虑一条线,设左右两边的颜色数量都为c,那么我们将这条线右移,左边加上了这一列的颜色钟数,右边减去了这一列的颜色种数,然后我们仍然要让左右保持相等,那么久表示,我们这一列的颜色必须填左边和右边都出现过的。
推广一下,发现除了最左边和最右边那两列以外其他的列都满足这个性质。
于是我们只用考虑左右两列就好了。
首先,很显然要枚举一个i,表示左右两边重复的颜色种数,那么对于所有在中间的方格都可以填这i种颜色中的任意一种。所以我们有 Ciki(m2)n 种方案。
接下来枚举一个j,表示左右两边各有另外j种不同的颜色,因为我们也要保证左右两边的颜色种数相同。
我们设cnt[i]表示用i种颜色填满这n个格子且每种颜色都会出现的方案数。
那么我们有 cnt[i+j]2CjkiCjkij 种方案。
因为左右两边是独立的,但要保证两边的这j种颜色都不相同。
最后我们考虑如何计算cnt。
显然可以容斥。

cnt[i]=j=1iCjiin(1)ij

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值