Codeforces 559 C Gerald and Giant Chess题解(计数DP)

题目:CF559C.
题目大意:给定一张 n ∗ m n*m nm的网格,要求从 ( 1 , 1 ) (1,1) (1,1)走到 ( n , m ) (n,m) (n,m),每次只能往下走一格或往右走一格.现在有 k k k个障碍点 ( x i , y i ) (x_i,y_i) (xi,yi),求不经过障碍点的方案数.
1 ≤ n , m ≤ 1 0 5 , 1 ≤ k ≤ 2000 1\leq n,m\leq 10^5,1\leq k\leq 2000 1n,m105,1k2000,答案对 1 0 9 + 7 10^9+7 109+7取模.

首先肯定是个DP,但 n , m n,m n,m过大,不能根据位置直接设计状态.

考虑补集转化,统计所有经过障碍点的方案数.为了不重复统计方案,设 f [ i ] f[i] f[i]表示到第一次经过障碍点时第 i i i个的方案数,显然转移方程为:
f [ i ] = ( x i + y i − 2 x i − 1 ) − ∑ i = ̸ j ∧ x j ≤ x i ∧ y j ≤ y i ( x i + y i − x j − y j x i − x j ) f [ j ] f[i]=\binom{x_i+y_i-2}{x_i-1}-\sum_{i=\not{}j\wedge x_j\leq x_i\wedge y_j\leq y_i}\binom{x_i+y_i-x_j-y_j}{x_i-x_j}f[j] f[i]=(xi1xi+yi2)i≠jxjxiyjyi(xixjxi+yixjyj)f[j]

时间复杂度 O ( n 2 ) O(n^2) O(n2).

代码如下:

#include<bits/stdc++.h>
using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=2000,M=100000,mod=1000000007;

int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int mul(int a,int b){return (LL)a*b%mod;}
void sadd(int &a,int b){a=add(a,b);}
void ssub(int &a,int b){a=sub(a,b);}
void smul(int &a,int b){a=mul(a,b);}

int n;
struct point{
  int x,y;
}a[N+9];

bool cmp(const point &a,const point &b){return a.x<b.x||a.x==b.x&&a.y<b.y;}

int inv[M*2+9],fac[M*2+9],ifac[M*2+9];

void Get_inv(){
  int m=a[n].x+a[n].y;
  inv[1]=1;
  fac[0]=fac[1]=1;
  ifac[0]=ifac[1]=1;
  for (int i=2;i<=m;++i){
  	inv[i]=mul(mod-mod/i,inv[mod%i]);
  	fac[i]=mul(fac[i-1],i);
  	ifac[i]=mul(ifac[i-1],inv[i]);
  }
}

int Get_c(int n,int m){return mul(fac[n],mul(ifac[m],ifac[n-m]));}

int dp[N+9];

void Get_dp(){
  dp[0]=1;
  for (int i=1;i<=n;++i){
    dp[i]=Get_c(a[i].x+a[i].y-2,a[i].x-1);
    for (int j=1;j<i;++j)
      if (a[j].y<=a[i].y)
	    ssub(dp[i],mul(dp[j],Get_c(a[i].x+a[i].y-a[j].x-a[j].y,a[i].x-a[j].x)));
  }
}

Abigail into(){
  int x,y;
  scanf("%d%d%d",&x,&y,&n);
  a[n+1].x=x;a[n+1].y=y;
  for (int i=1;i<=n;++i)
    scanf("%d%d",&a[i].x,&a[i].y);
}

Abigail work(){
  a[0].x=1;a[0].y=1;
  sort(a+1,a+n+1,cmp);
  ++n;
  Get_inv();
  Get_dp();
}

Abigail outo(){
  printf("%d\n",dp[n]);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值