Code Forces 559 C. Gerald and Giant Chess(组合数学+dp)

Description
一块h*w的棋盘,上面有n个黑块,现在处于左上角的棋子只能往下或往右走,且不能经过黑块,问走到终点的路径有多少种
Input
第一行三个整数h,w和n,之后n行每行两个整数x和y表示该黑块坐标
Output
输出到达终点的路径条数,结果模1e9+7
Sample Input
3 4 2
2 2
2 3
Sample Output
2
Solution
因为此题h和w数据量过大,搜索显然不行,因黑块最多2000个,所以考虑以黑块为突破点利用dp求解路径条数,容易得知以某黑块为终点,所有可以不经过其他黑块到达该黑块的路径数起点到达该黑块的路径数减去起点到达这个黑块左上方某黑块的不经过其他黑块的路径条数乘上这个黑块左上方的黑块到达这个黑块的路径数的累加
Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 200005
typedef long long ll;
const long long p=1000000007ll;
ll rev[maxn];
ll power[maxn];
ll dp[2222];
struct node
{
    int x;
    int y;
}pot[2222];
ll mod_pow(ll a,ll b,ll m)//快速幂 
{
    ll ans=1;
    a%=m;
    while(b)
    {
        if(b&1)ans=ans*a%m;
        b>>=1;
        a=a*a%m;
    }
    return ans;
}
void init()//预处理出n的阶乘及其阶乘的逆元 
{
    power[0]=1;
    for(int i=1;i<maxn;i++)
        power[i]=power[i-1]%p*i%p;
    rev[1]=1;
    for(int i=0;i<maxn;i++) 
        rev[i]=mod_pow(power[i],p-2,p);
}
ll f(int n,int m)//求组合数 
{
    if(m==0||n-m==0)
        return 1;
    return (ll)power[n]*rev[m]%p*rev[n-m]%p;
}
int cmp(node a,node b)//排序函数 
{
    if(a.x!=b.x)//先排横坐标 
        return a.x<b.x;
    return a.y<b.y;//横坐标相同则排纵坐标 
}
ll solve(int x1,int y1,int x2,int y2)//求(x1,y1)到(x2,y2)的所有路径数 
{
    int x=x2-x1+1;
    int y=y2-y1+1;
    return f(x+y-2,x-1);
}
int main()
{
    init();
    int h,w,n;
    cin>>h>>w>>n;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&pot[i].x,&pot[i].y);
    pot[n+1].x=h;//将终点也看作一个黑块 
    pot[n+1].y=w;
    n++;
    sort(pot+1,pot+n+1,cmp);//对黑块位置排序 
    for(int i=1;i<=n;i++)//预处理出起点到达某黑块的所有路径数 
        dp[i]=solve(1,1,pot[i].x,pot[i].y);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            if(pot[j].y<pot[i].y)
                continue;
            dp[j]-=(dp[i]*solve(pot[i].x,pot[i].y,pot[j].x,pot[j].y))%p;
            if(dp[j]<0)dp[j]+=p;
        }
    printf("%d\n",dp[n]);
    return 0;
}

你可能感兴趣的:(Code Forces 559 C. Gerald and Giant Chess(组合数学+dp))