• Codeforces 1237F. Balanced Domino Placements


    传送门

    很妙的题

    首先先考虑一个简化的问题,现在有一行格子让你填

    你要么填一格 要么填两格 有的格子不让你填 问你填了 $a$ 个一格和填了 $b$ 个两格有多少种方案

    那么显然先只考虑放两格的方案,这个可以简单 $dp$ 得到,设 $f[i][j]$ 表示前 $i$ 个格子放了 $j$ 个两格的方案数

    那么如果 $i,i-1$ 都没障碍,那么 $f[i][j]=f[i-1][j]+f[i-2][j-1]$ ,否则 $f[i][j]=f[i-1][j]$

    然后再来考虑填一格的,显然剩下的 $tot-2j$ 个位置都可以随便填,那么方案数为 $C[tot-2j][i]$ ,直接乘法原理乘起来即可

    接下来可以考虑怎么把这道题简化到这个情况,假设放了 $a$ 个水平的多米诺,$b$ 个垂直的多米诺

    对于行来说,相当于放 $a$ 个 $1$ ,$b$ 个 $2$,对于列就相当于放 $a$ 个 $2$ ,$b$ 个 $1$

    注意到每个多米诺可以根据在第几行和第几列来唯一确定,所以我们对行列分别求一下之前那个东西然后乘起来再乘上 $a!b!$ 即可

    乘上 $a!b!$ 就相当于把骨牌不同的放置顺序看成不同的放置方案,意思是强制行的第 $i$ 个放置和列的第 $i$ 个放置配对

    就原本一维的方案我们让它放置有顺序,然后强制行和列两两匹配,这样才能确定二维平面上的具体位置

    因为如果只是行列乘起来,那么没法确定某个骨牌的具体位置,考虑对于 $a$ ,行放了位置 $1,3$ ,列放了位置 $(1,2),(3,4)$

    那么多米诺骨牌可以是 $((1,1),(1,2))$ 和 $((3,3),(3,4))$ ,但是也有可能是 $((1,3),(1,4))$ 和 $((3,1),(3,2))$

    自己画画图就很容易理解了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=4007,mo=998244353;
    inline int fk(int x) { return x>=mo ? x-mo : x; }
    int n,m,K,Ans,fac[N],C[N][N];
    int f[N][N],g[N][N];
    bool px[N],py[N];
    int main()
    {
        n=read(),m=read(),K=read(); int mx=max(n,m);
        fac[0]=1; for(int i=1;i<=mx;i++) fac[i]=1ll*fac[i-1]*i%mo;
        for(int i=0;i<=mx;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=i;j++)
                C[i][j]=fk(C[i-1][j-1]+C[i-1][j]);
        }
        for(int i=1;i<=K;i++)
        {
            int a=read(),b=read(),c=read(),d=read();
            px[a]=px[c]=1; py[b]=py[d]=1;
        }
        int cntx=0,cnty=0;
        for(int i=1;i<=n;i++) cntx+=px[i];
        for(int i=1;i<=m;i++) cnty+=py[i];
        for(int i=0;i<=n;i++) f[i][0]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<=mx;j++)
                if(px[i]||px[i-1]) f[i][j]=f[i-1][j];
                else f[i][j]=fk(f[i-1][j]+f[i-2][j-1]);
        for(int i=0;i<=m;i++) g[i][0]=1;
        for(int i=2;i<=m;i++)
            for(int j=1;j<=mx;j++)
                if(py[i]||py[i-1]) g[i][j]=g[i-1][j];
                else g[i][j]=fk(g[i-1][j]+g[i-2][j-1]);
        for(int i=0;i<=mx;i++)
            for(int j=0;j<=mx;j++)
                if(i+j*2<=n-cntx&&i*2+j<=m-cnty)
                    Ans=fk(Ans + 1ll*f[n][j]*C[n-cntx-j*2][i]%mo *g[m][i]%mo *C[m-cnty-i*2][j]%mo *fac[i]%mo *fac[j]%mo );
        printf("%d
    ",Ans);
        return 0;
    }
  • 相关阅读:
    2020以去过半,写一下上半年的总结跟下半年的计划
    js实现浏览器打印功能
    看不见远程新建git分支
    Vue中导出Excel表格方法
    SVN命令使用详解
    IOS NSTimer 定时器用法总结
    静态库与动态库的区别?
    iOS 本地缓存实现 方案借鉴
    IOS开发中NSRunloop跟NSTimer的问题
    FMDB
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11697525.html
Copyright © 2020-2023  润新知