Codeforces 559C - Gerald and Giant Chess 【计数DP】

迷南。 2023-10-11 13:02 120阅读 0赞

题目描述

假设虚伪有一个h行w列的棋盘,棋盘上的格子有的是可以经过的,有的是不可以经过的。一开始在棋盘的左上角(第一行第一列)有一颗棋子,这颗棋子每次只能往右或者往下移动一格。那么虚伪要将其从起点移动到右下角( h,w) 一共有多少种移法?

输入

第一行输入三个整数:h,w,n (1<=h,w<=10^5,1<=n<=2000)。

分别代表h行w列的棋盘,棋盘上一共有n个不可经过的点。接下来n行中,每一行有两个整数xi,yi。代表在棋盘中的第xi行,第yi列所在的格子是不可经过的。

输出

输出移动的方法数,结果请对1000000007取余数。

思路:不考虑黑色格子的话就是一个经典DP问题,对于n行m列来说,左上走到右下的方案数就是C(n + m - 2, n - 1) , 因为总共要走n + m - 2步,向下n-1次,向右m-1次,所以就是它们组成的序列,它们之间的顺序是任意的,所以就得到一个排列式。

将黑色格子按照x升序,y升序排序,我们设F[i] 表示第一次走到的黑色格子是第i个的方案数,那么对于任意i

F[i] = F[i] - F[j] * C(xi + yi - xj - yj, xi - xj) (j < i) , 其中F[i] = C(xi + yi - 2, xi - 1), 目标就是F[n + 1]

可以这样理解它的状态,对于任意的i,我们需要去除重复计算的,就是之前经过某个黑色格子j,方案是F[j],然后从j到i的方案又是C(xi + yi - xj - yj, xi - xj), 所以F[i] - F[j] * C(xi + yi - xj - yj, xi - xj).

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. using namespace std;
  6. typedef long long ll;
  7. #define ls rt << 1
  8. #define rs rt << 1|1
  9. #define mid ((l + r) >> 1)
  10. #define lson l, mid, ls
  11. #define rson mid + 1, r, rs
  12. const int maxn = 2e5 + 10;
  13. const int mod = 1e9 + 7;
  14. ll p[maxn], q[maxn]; //p是阶乘,q是阶乘的逆元
  15. ll powermod(ll a, ll b, ll c)
  16. {
  17. ll ans = 1;
  18. a = a % c;
  19. while(b)
  20. {
  21. if(b&1)
  22. ans = ans * a % c;
  23. b >>= 1;
  24. a = a * a % c;
  25. }
  26. return ans % c;
  27. }
  28. void init()
  29. {
  30. p[0] = q[0] = 1;
  31. for(int i = 1; i < maxn; ++i)
  32. p[i] = (p[i - 1] * i) % mod;
  33. q[maxn - 1] = powermod(p[maxn - 1], mod - 2, mod);
  34. for(int i = maxn - 1; i > 0; --i)
  35. q[i - 1] = q[i] * i % mod;
  36. }
  37. ll C(ll n, ll m) //C(n, m) n是底数
  38. {
  39. if(n < m)
  40. return 0;
  41. return p[n] * q[m] % mod * q[n - m] % mod;
  42. }
  43. struct node
  44. {
  45. ll x, y;
  46. bool operator < (const node &r) const
  47. {
  48. if(x == r.x) return y < r.y;
  49. return x < r.x;
  50. }
  51. }a[maxn];
  52. ll f[maxn];
  53. int main()
  54. {
  55. init();
  56. ll h, w, n;
  57. scanf("%lld%lld%lld", &h, &w, &n);
  58. for(int i = 1; i <= n; ++i)
  59. scanf("%lld%lld", &a[i].x, &a[i].y);
  60. sort(a + 1, a + n + 1);
  61. a[n + 1].x = h, a[n + 1].y = w;
  62. for(int i = 1; i <= n + 1; ++i)
  63. {
  64. f[i] = C(a[i].x + a[i].y - 2, a[i].x - 1);
  65. for(int j = 1; j < i; ++j)
  66. {
  67. if(a[j].x <= a[i].x && a[j].y <= a[i].y)
  68. f[i] = (f[i] - f[j] * C(a[i].x + a[i].y - a[j].x - a[j].y, a[i].x - a[j].x) % mod + mod) % mod;
  69. }
  70. }
  71. ll ans = (f[n + 1] + mod) % mod;
  72. printf("%lld\n", ans);
  73. return 0;
  74. }

发表评论

表情:
评论列表 (有 0 条评论,120人围观)

还没有评论,来说两句吧...

相关阅读

    相关 [CF559 C]Gerald and Giant Chess

    题面描述 给定一个\\(H\W\\)的棋盘,棋盘上只有\\(N\\)个格子是黑色的,其他格子都是白色的。在棋盘左上角有一个卒,每一步可以向右或者向下移动一格,并且不能移动

    相关 计数dp

    描述:计算从1到n中,每个数字(0到9)出现的次数 其中sum[j]和dp[i]表示:数字i中 j 的个数;比如sum[1]和dp[5]就可以表示:5中1的