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=,y=;char c=getchar();
while(c<''||c>''){if(c=='-')y=-;c=getchar();}
while(c>=''&&c<=''){x=(x<<)+(x<<)+c-'';c=getchar();}
return x*y;
}
const int N=,M=;
int h,w,n,mod=(1e9)+;
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=;
while(y){if(y&)s=(s*x)%mod;x=(x*x)%mod;y>>=;}
return s;
}
il void init_C()
{
jc[]=,inv[]=;
go(i,,)
jc[i]=jc[i-]*i%mod,inv[i]=ksm(jc[i],mod-);
}
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,,n){a[i]=(node){read(),read()},a[i].s=a[i].x+a[i].y;}
a[n+]=(node){h,w,h+w};
sort(a+,a+n+,cmp);
go(i,,n+)
{
f[i]=C(a[i].s-,a[i].x-);
go(j,,i-)
{
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\n",(f[n+]+mod)%mod);
return ;
}

随机推荐

  1. Vue.js 第3章 axios&Vue过渡动画

    promise 它将我们从回调地狱中解脱出来 创建和使用 var fs = require('fs') // 创建promise // reslove表示执行成功后调用的回调函数 // reject表 ...

  2. Javascript中的定时调用函数setInterval()和setTimeout()

    首先介绍这两个函数 一.setInterval() 按照指定的周期来调用函数或表达式,执行多次.(时间单位:ms) timer = setInterval("content =documen ...

  3. 容器安全拾遗 - Rootless Container初探

    摘要: Docker和Kubernetes已经成为企业IT架构的基础设施,安全容器运行时越来越被关注.近期Docker 19.03中发布了一个重要的特性 “Rootless Container”,在提 ...

  4. Android 高仿微信(QQ)滑动弹出编辑、删除菜单效果,增加下拉刷新功能

    不可否认,微信.QQ列表的滑动删除.编辑功能着实很经典(从IOS那边模仿过来的),然.Android这边,对列表的操作,其实大多还停留上下文菜单来实现. Android如何实现list item的滑动 ...

  5. poj1741 树上距离小于等于k的对数 点分治 入门题

    #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm& ...

  6. 重置Mysql自增列的开始序号

    ALTER TABLE  TableName AUTO_INCREMENT = 5; 代表重新从5开始(包括5)

  7. POJ2406 Power Strings 题解 KMP算法

    题目链接:http://poj.org/problem?id=2406 题目大意:给你一个字符串 \(t\) ,\(t\) 可以表示为另一个小字符串循环了 \(K\) 了,求最大的循环次数 \(K\) ...

  8. SuperSocket通过 SessionID 获取 Session

    前面提到过,如果你获取了连接的 Session 实例,你就可以通过 "Send()" 方法向客户端发送数据.但是在某些情况下,你无法直接获取 Session 实例. SuperSo ...

  9. Java语法格式

    任何一种语言都有自己的语法规则,Java也一样,既然是规则,那么知道其如何使用就可以了. 代码都定义在类中,类由class来定义,区分 public class  和  class; 代码严格区分大小 ...

  10. Flex AIR应用GPS定位功能(Android和IOS)

    说明: 使用AIR进行GPS定位功能实现时,会经常判断GPS是否打开.一般的官方或者书上的介绍的方法,测试后,只能对Android系统进行判断,而对ios系统则无法进行判断. 经过研究测试,终于解决实 ...