Codeforces #313 (Div. 1) C. Gerald and Giant Chess dp 组合数 逆元
2015-09-21 12:44
387 查看
描述
有一个 h×wh \times w 的棋盘,需要从左上角走到右下角。每次只能向右或者向下走一步。其中有 nn 个格子被标记成不能就经过,这些格子的坐标为(ri,ci)(r_i, c_i)。问走到右下角可以有多少种方案数。(1≤h,w≤105,1≤n≤2000,1≤ri≤j,1≤ci≤w,ansmod109+7)(1 \le h,w \le 10^5, 1 \le n \le 2000, 1 \le r_i \le j, 1 \le c_i\le w,ans \mod 10^9+7)
思路
一开始的思路是容斥原理,但是公式推了半天都推不出来。最后经过指导得出了解法。记从(x1,y1)(x_1, y_1)到(y1,y2)(y_1, y_2) 的方案数为 f(x1,y1,x2,y2)=c(x2−x1+y2−y1,y2−y1)f(x_1,y_1, x_2, y_2) = c(x_2-x_1+y_2-y_1, y_2-y_1)。
记dp(x,y)dp(x, y)为到从起点到(x,y)(x, y)点并且不经过被标记的点的方案数。
直接算dp(x,y)dp(x, y)确实不好算,我们可以算出总共的,再减去不合法的。总共的值为f(1,1,x,y)f(1, 1, x, y),对于(1,1)(1, 1)到(x,y)(x, y) 之间的所有的被标记的点(ri,ci)(r_i, c_i)那么一它为第一个经过的被标记的点的方案数是dp(ri,ci)×f(ri,ci,x,y)dp(r_i, c_i) \times f(r_i, c_i, x, y)。
那么
dp(x,y)=f(1,1,x,y)−∑(dp(ri,ci)×f(ri,ci,x,y))dp(x, y) = f(1, 1, x, y) - \sum(dp(r_i, c_i) \times f(r_i, c_i, x, y))
于是我们要计算的是dp(h,w)dp(h, w);
code
[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 ; 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 ); return 0; }
相关文章推荐
- hdoj 3466 Proud Merchants
- OCP-V13-707
- STM32-以太网学习之路-STM32F407开发
- 0916 编译原理第二次上机作业
- 用c语言编写打印出100~200 之间的素数。
- 一段c++输入格式解析代码
- 【bzoj3505】 CQOI2014数三角形 数学
- C++11:移动语义和完美转发
- eclipse工作空间无法打开
- 西窗的雨
- makefile---使用共享库文件so
- 2015程序员值得收藏的十大主流button模式
- 闭包Closures
- untiy3d实现简单转盘抽奖
- 桌面协议(1)rdesktop 24位色
- 图片圆角化处理
- 关于C++中如何判断文件,目录存在的若干方法
- 杭电1027Ignatius and the Princess II
- Linux内核编码规范
- Android Studio学习 ——如何找到当前类/方法被引用位置