Codeforces #313 (Div. 1) C. Gerald and Giant Chess dp 组合数 逆元

来源:互联网 发布:上古5白雪sg捏脸数据 编辑:程序博客网 时间:2024/05/17 09:48

描述

有一个 h×w 的棋盘,需要从左上角走到右下角。每次只能向右或者向下走一步。其中有 n 个格子被标记成不能就经过,这些格子的坐标为(ri,ci)。问走到右下角可以有多少种方案数。
(1h,w105,1n2000,1rij,1ciw,ansmod109+7)

思路

一开始的思路是容斥原理,但是公式推了半天都推不出来。最后经过指导得出了解法。
记从(x1,y1)(y1,y2) 的方案数为 f(x1,y1,x2,y2)=c(x2x1+y2y1,y2y1)
dp(x,y)为到从起点到(x,y)点并且不经过被标记的点的方案数。
直接算dp(x,y)确实不好算,我们可以算出总共的,再减去不合法的。总共的值为f(1,1,x,y),对于(1,1)(x,y) 之间的所有的被标记的点(ri,ci)那么一它为第一个经过的被标记的点的方案数是dp(ri,ci)×f(ri,ci,x,y)

那么

dp(x,y)=f(1,1,x,y)(dp(ri,ci)×f(ri,ci,x,y))

于是我们要计算的是dp(h,w);


code

#include <bits/stdc++.h>using namespace std;const int MOD = 1E9+7;const int maxn=300005;long long fac[maxn];long long inv[maxn];/*=================================================*/long long power(long long a,long long p) {      long long res=1;      while(p) {          if(p&1)              res=(res*a)%MOD;          a=(a*a)%MOD;          p>>=1;      }      return res;  }  long long Inv(long long a) {      return power(a,MOD-2);  }  void init() {      fac[0]=inv[0]=1;      for(long long i=1;i<300005;i++) {          fac[i]=fac[i-1]*i;          fac[i]%=MOD;          inv[i]=Inv(fac[i]);      }  }  long long C(long long n,long long m) {      if(n<0||m<0)          return 0;      long long res=fac[n];    res%=MOD;      res*=inv[m];    res%=MOD;    res*=inv[n-m];    res%=MOD;    return res;}  /*=================================================*/struct Node {    int x, y;    Node() {}    Node(int _x, int _y) {        x = _x;        y = _y;    }}p[2005];bool cmp (Node a, Node b) {    if (a.x == b.x) return a.y < b.y;    else return a.x < b.x;}int h, w, n;long long res = 0;long long dp[2005];int main () {    init();    scanf("%d%d%d", &h, &w, &n);    p[0] = Node(h, w);    for (int i=1; i<=n; i++) {        scanf ("%d%d", &p[i].x, &p[i].y);    }    sort(p, p+n+1, cmp);    dp[0] = C(p[0].x + p[0].y - 2, p[0].x-1);    for (int i=1; i<=n; i++) {        dp[i] = C(p[i].x - 1 + p[i].y - 1, p[i].x - 1);        res = 0;        for (int j=0; j<i; j++) {            if (p[j].x <= p[i].x && p[j].y <= p[i].y) {                res = (res + (dp[j] * C(p[i].x - p[j].x + p[i].y - p[j].y, p[i].x - p[j].x)) % MOD ) % MOD;            }        }        dp[i] = (dp[i] - res + MOD) % MOD;    }    printf("%d\n", dp[n]);    return 0;}
0 0
原创粉丝点击