【Codeforces Round #313 (Div. 1) C】 CodeForces 559C Gerald and Giant Chess

来源:互联网 发布:aws ec2 centos 编辑:程序博客网 时间:2024/06/05 10:25

Description

Giant chess is quite common in Geraldion. We will not delve into the
rules of the game, we’ll just say that the game takes place on an
h × w field, and it is painted in two colors, but not like in chess.
Almost all cells of the field are white and only some of them are
black. Currently Gerald is finishing a game of giant chess against his
friend Pollard. Gerald has almost won, and the only thing he needs to
win is to bring the pawn from the upper left corner of the board,
where it is now standing, to the lower right corner. Gerald is so
confident of victory that he became interested, in how many ways can
he win?

The pawn, which Gerald has got left can go in two ways: one cell down
or one cell to the right. In addition, it can not go to the black
cells, otherwise the Gerald still loses. There are no other pawns or
pieces left on the field, so that, according to the rules of giant
chess Gerald moves his pawn until the game is over, and Pollard is
just watching this process.

Input

The first line of the input contains three integers: h, w, n — the
sides of the board and the number of black cells
(1 ≤ h, w ≤ 105, 1 ≤ n ≤ 2000).

Next n lines contain the description of black cells. The i-th of these
lines contains numbers ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w) — the number of
the row and column of the i-th cell.

It is guaranteed that the upper left and lower right cell are white
and all cells in the description are distinct.

Output

Print a single line — the remainder of the number of ways to move
Gerald’s pawn from the upper left to the lower right corner modulo
109 + 7.

计数类dp。
用总方案数减去经过黑格子的方案个数,得到的就是答案。
但是经过黑格子的路径可能经过很多个黑格子,导致重复计算。
于是可以设dp[i]表示从起点到黑点i的合法路径个数,合法路径是说这之前不经过其他黑点。
那么dp[i]=(起点走到点i的路径总数)-∑(dp[j]*(点j到点i的路径总数),点j在点i的左上方)
可以把终点当成一个黑点,他的dp就是答案。
解决两个问题:
1.保证计算点i时,所有在点i左上方的点都计算过。可以通过坐标排序解决。
2.求两点之间的路径总数。
设两点的横、纵坐标之差分别为x,y。
路径总数=C(x,x+y)=(x+y)!/(x!y!)。
因为有除法,所以需要用乘法逆元。
预处理出1..20000的阶乘和其逆元即可。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const long long p=1000000007;long long dp[2010],jc[200100],vjc[200100];struct node{    int x,y;    bool operator < (const node &a) const    {        return (x<a.x)||(x==a.x&&y<a.y);    }}a[2010];void eu(long long a,long long b,long long &x,long long &y){    if (!b)    {        x=1;        y=0;    }    else    {        eu(b,a%b,y,x);        y-=x*(a/b);    }}void init(){    int i;    vjc[0]=jc[0]=1;    long long x;    for (i=1;i<=200010;i++)    {        jc[i]=(jc[i-1]*i)%p;        eu(jc[i],p,vjc[i],x);        vjc[i]%=p;        vjc[i]+=p;        vjc[i]%=p;    }}long long c(int x,int y){    return jc[y]*vjc[x]%p*vjc[y-x]%p;}int main(){    int i,j,k,m,n,q,x,y,z,h,w;    init();    scanf("%d%d%d",&h,&w,&n);    n++;    for (i=1;i<n;i++)      scanf("%d%d",&a[i].x,&a[i].y);    a[n].x=h;    a[n].y=w;    sort(a+1,a+n+1);    for (i=1;i<=n;i++)    {        dp[i]=c(a[i].x-1,a[i].x+a[i].y-2);        for (j=1;j<i;j++)          if (a[j].y<=a[i].y)            dp[i]=((dp[i]-dp[j]*c(a[i].x-a[j].x,a[i].x-a[j].x+a[i].y-a[j].y)%p)+p)%p;    }    printf("%lld\n",dp[n]);}
1 0