【codeforces 559C】【51nod 1486】大大走格子【容斥】

Description

一个棋盘,h*w,n个不能走的格子,从左上走到右下,求方案数。
(h,w < 100000,n < 2000)

Solution

  先将坏格排个序,然后考虑第i个坏格,用 f[i] 表示在不经过其他坏格的情况下,走到第i个坏格的方案数。
  
  假如不存在坏格,从 (1,1) 走到 (n,m) 的共有 C(n+m2,n1) 种方法。
  
  怎么证明呢?
  n行m列,只能向下或向右走,那么我们一共要向下走n-1次,向右走m-1次,那么我们从左上走到右下的路径就可以表示为由n-1次向下和m-1次向右组成的序列,而这个序列的顺序是无所谓的,只要是n-1次向下和m-1次向右我们一定能且会走到右下角,所以这就是一个组合问题了。
  
  然后考虑如何求 f[i]
  减掉不不合法的,枚举第⼀次经过的坏格为 j。 f[i]=f[j]C(xixj+yiyjyiyj)
  
  我还看到一个贼详细的题解,你要是看不懂我的话可以看这个复杂一点的:穿梭吧

Code

#include<cstdio>
#include<cstring>
#include<algorithm>

#define N 2010
#define H 100010
using namespace std;
typedef long long ll;
const ll mod = 1000000007;

struct node{int x,y;}d[N];
bool cmp(node a,node b){return a.x == b.x ? a.y < b.y : a.x < b.x;}
ll mul[2*H],inv[2*H],f[N];

ll q_power(ll a,int b)
{
    ll res = 1,x = a;
    while(b) {
        if(b & 1) res = res * x % mod;
        x = x * x % mod;
        b >>= 1;
    }
    return res % mod;
}

#define x1 d[i].x
#define y1 d[i].y
#define x2 d[j].x
#define y2 d[j].y

ll calc(ll n,ll m){
    return (mul[n] % mod * inv[n-m] % mod * inv[m] % mod)%mod;
}

int main()
{
    int h,w,n;
    scanf("%d%d%d",&h,&w,&n);
    for(int i = 1;i <= n;i++) scanf("%d%d",&d[i].x,&d[i].y);
    d[++n] = (node){h,w};

    mul[0] = inv[0] = 1;
    for(int i = 1;i <= h+w+2;i++) {
        mul[i] = ((ll)i * mul[i-1]) % mod;
        inv[i] = q_power(mul[i],mod-2) % mod;
    }
    sort(d+1,d+n+1,cmp);

    for(int i = 1;i <= n;i++) {
        f[i] = calc(x1+y1-2,y1-1);
        for(int j = 1;j < i;j++)
            if(x2 <= x1 && y2 <= y1)
                f[i] = (f[i]-f[j]*calc(x1-x2+y1-y2,y1-y2)%mod+mod)%mod;
    }
    printf("%I64d\n",f[n]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值