【组合数学】【DP】三校联考 10.15 —— Chess

本文探讨了一个关于组合数学的问题,即在一个n*m的棋盘上,如何计算满足每个n*n区域有K个棋子的不同摆放方案的数量。通过观察发现,棋盘前n列的摆放决定了后续列的循环模式,因此只需关注这n列。文章给出了计算公式,并介绍了使用动态规划统计所有可能情况的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

dirty 在一个棋盘上放起了棋子。
棋盘规格为 n ∗ m,他希望任意一个 n ∗ n 的区域内都有 K 个棋子。dirty 很快就放置好了一
个满足条件的棋盘方案,但是他认为这样过于简单了,他希望知道有多少个满足条件的方案。

看题第一眼即可知道是关于组合数学的.
通过观察发现,如果棋盘上前n列已经定了,那么之后的情况只会是前面n列的循环.因此只需考虑n列.
可以得出以下式子
sum=Ck1n⋅Ck2n⋅Ck3n⋯Cknnsum=C_{k1}^{n} \cdot C_{k2}^{n} \cdot C_{k3}^{n} \cdots C_{kn}^{n}sum=Ck1nCk2nCk3nCknn
其中K=k1+k2+⋯+knK=k1+k2+\cdots+knK=k1+k2++kn
这个sumsumsum的含义是在每一列的棋子数固定的情况下前n列的方案数.
那么这种情况对于答案的贡献为
sum⌊mn⌋⋅Ck1n⋅Ck2n⋅Ck3n⋯Ck m mod nnsum^{\lfloor\frac{m}{n}\rfloor}\cdot C_{k1}^{n} \cdot C_{k2}^{n} \cdot C_{k3}^{n} \cdots C_{k\ _{m \ mod\ n}}^{n}sumnmCk1nCk2nCk3nCk m mod nn
然后将所有的情况利用dp统计.(详见代码)
题目时间限制比较卡,注意预处理出组合数.

#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long ll;
const int MOD=1e9+7;
const int MAXN=105;
const int MAXM=10005;

int n,c;
ll m,x1,x2;
ll fac[MAXM],finv[MAXM];
ll d[MAXN][MAXM],f[2][MAXN];

void Init(){
    fac[0]=1;
    for(int i=1;i<MAXM;i++) fac[i]=fac[i-1]*i%MOD;
    finv[1]=1;
    for(int i=2;i<MAXM;i++) finv[i]=(MOD-MOD/i)*finv[MOD%i]%MOD;
    finv[0]=1;
    for(int i=1;i<MAXM;i++) finv[i]=finv[i-1]*finv[i]%MOD;
}

ll C(ll x,ll y){
    if(x<y) return 0;
    return (fac[x]*finv[y]%MOD)*finv[x-y]%MOD;
}

ll ksm(ll x,ll y){
    y%=(MOD-1);
    ll res=1;
    while(y){
        if(y&1) res=res*x%MOD;
        x=x*x%MOD;
        y>>=1ll;
    }
    return res;
}

int main(){
    freopen("chess.in","r",stdin);
    freopen("chess.out","w",stdout);

    Init();
    scanf("%d %lld %d",&n,&m,&c);
    if(m<n) return printf("%lld",ksm(2,n*m)),0;
    x1=m/n,x2=m%n;
    d[n+1][0]=1;
    for(int i=0;i<=n;i++) f[0][i]=ksm(C(n,i),x1),f[1][i]=ksm(C(n,i),x1+1);
    for(int i=n;i>=1;i--)
        for(int j=c;j>=0;j--){
            for(int k=0;k<=min(n,j);k++)
                d[i][j]=(d[i][j]+d[i+1][j-k]*(i<=x2?f[1][k]:f[0][k])%MOD)%MOD;
            if(i==1) break;
        }
    printf("%lld\n",d[1][c]);

    fclose(stdin);
    fclose(stdout);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值