• $CF559C Gerald and Fiant Chess$ 计数类$DP$


    AcWing

    Description

    有个$H$行$W$列的棋盘,里面有$N$个黑色格子,求一个棋子由左上方格子走到右下方格子且不经过黑色格子的方案数.

    $1<=H,M<=1e5,1<=N<=2000$.输出对$1e9+7$去模后的结果即可

    Sol

    假设没有黑色格子,方案数就为$C_{H+W-2}^{H-1}$.

    简单说下,可以把向下走看做$0$,向右走看做$1$,其实就是求$01$序列的种数

    注意到,黑色格子的总数相当少,所以我们可以把求不经过黑色格子的方案数转化成总方案数减去至少经过一个黑色格子的方案数

    在求解计数类$DP$问题时,通常要找到一个"基准点",围绕这个基准点构造一个不可划分的"整体",以避免子问题的重叠.

    这句话的意思大概就是找到标准把问题划分为子问题,且这些子问题具有互斥性.

    将统计至少经过一个黑色格子的问题转化为枚举每一个黑色格子,并且将以该黑色格子为第一个经过的黑色格子的路径方案数相加.(定语似乎太长了,但是应该都懂的叭)

    具体来说,将所有的黑色格子按照行,列坐标递增的顺序排序.第$i$个黑色格子在第$xi$行,$yi$列,设$F[i]$表示从左上角走到第$i$个黑色格子,并且途中不经过其他黑色格子的路线数.

    $F[i]=C_{xi-1+yi-1}^{xi-1}-sum_{j=0}^{i-1}F[j]*C_{xi-xj+yi-yj}^{xi-xj}$,其中$xi>=xj,yi>=yj$

    特别地,我们假设左上角的格子为第$0$个黑色格子,右下角的格子为第$N+1$个黑色格子.以$F[0]=1$为初值,$F[n+1]$为答案.

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define il inline
    #define Rg register
    #define go(i,a,b) for(Rg int i=a;i<=b;i++)
    #define yes(i,a,b) for(Rg int i=a;i>=b;i++)
    #define ll long long
    using namespace std;
    il int read()
    {
        int x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=2010,M=200010;
    int h,w,n,mod=(1e9)+7;
    ll f[N],jc[M],inv[M];
    struct node{int x,y,s;}a[N];
    il bool cmp(node x,node y){if(x.x==y.x)return x.y<y.y;return x.x<y.x;}
    il ll ksm(ll x,int y)
    {
        ll s=1;
        while(y){if(y&1)s=(s*x)%mod;x=(x*x)%mod;y>>=1;}
        return s;
    }
    il void init_C()
    {
        jc[0]=1,inv[0]=1;
        go(i,1,200000)
            jc[i]=jc[i-1]*i%mod,inv[i]=ksm(jc[i],mod-2);
    }
    il ll C(int x,int y)
    {
        return jc[x]*inv[y]%mod*inv[x-y]%mod;
    }
    int main()
    {
        h=read(),w=read(),n=read();init_C();
        go(i,1,n){a[i]=(node){read(),read()},a[i].s=a[i].x+a[i].y;}
        a[n+1]=(node){h,w,h+w};
        sort(a+1,a+n+1,cmp);
        go(i,1,n+1)
        {
            f[i]=C(a[i].s-2,a[i].x-1);
            go(j,1,i-1)
            {
                if(a[j].x>a[i].x || a[j].y>a[i].y)continue;
                f[i]=(f[i]-f[j]*C(a[i].s-a[j].s,a[i].x-a[j].x))%mod;
            }
        }
        printf("%lld
    ",(f[n+1]+mod)%mod);
        return 0;
    }
    View Code

     

    光伴随的阴影
  • 相关阅读:
    【读书笔记-《Android游戏编程之从零开始》】2.Hello,World!
    【读书笔记-《Android游戏编程之从零开始》】1.Android 平台简介与环境搭建
    .Net HttpClient 模拟登录微信公众平台发送消息
    C# DateTime的ToString()方法的使用
    SQL 2008R2 日期转换
    Dojo学习(一)—Hello Dojo
    【博客开篇】服务器配置:Windows2008R2+PHP5.6+SQLServer2008(X64)
    【翻译Autofac的帮助文档】1.入门指南
    申请免费的SSL证书(Win7,PowerShell,Let's Encrypt)
    Rms操作设置office系统文档权限
  • 原文地址:https://www.cnblogs.com/forward777/p/11254102.html
Copyright © 2020-2023  润新知