codeforces 560E dp+组合数学

题意

在一个二维坐标系中,起点在 ( 1 , 1 ) (1,1) (1,1),终点在 ( h , w ) (h,w) (h,w),期间只能向上,向右,斜向上
并且不能经过坏点的方案数。坏点有n个

分析

我们分析两个坏点之间的方案数: C x i − x j + y i − y j x i − x j C_{x_i-x_j+y_i-y_j}^{x_i-x_j} Cxixj+yiyjxixj
我们定义 d p [ i ] dp[i] dp[i]为从 ( 1 , 1 ) (1,1) (1,1)点到第 i i i个坏点之间不经过坏点的方案数
那我们知道 d p [ 0 ] = C x 0 − 1 + y 0 − 1 x 0 − 1 dp[0]=C_{x_0-1+y_0-1}^{x_0-1} dp[0]=Cx01+y01x01

d p [ i ] = C x i − 1 + y i − 1 x i − 1 + ∑ j = 1 i − 1 d p [ j ] ∗ C x i − x j + y i − y j x i − x j dp[i]=C_{x_i-1+y_i-1}^{x_i-1}+\sum_{j=1}^{i-1}dp[j]*C_{x_i-x_j+y_i-y_j}^{x_i-x_j} dp[i]=Cxi1+yi1xi1+j=1i1dp[j]Cxixj+yiyjxixj

我们对所有的坏点进行排序(点 ( h , w ) (h,w) (h,w)也算一个坏点),然后跑到最后一个坏点的dp值即为答案

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
void out(T x) { cout << x << endl; }
ll fast_pow(ll a, ll b, ll p) {ll c = 1; while(b) { if(b & 1) c = c * a % p; a = a * a % p; b >>= 1;} return c;}
ll exgcd(ll a, ll b, ll &x, ll &y) { if(!b) {x = 1; y = 0; return a; } ll gcd = exgcd(b, a % b, y, x); y-= a / b * x; return gcd; }
const int N = 2e3 + 5;
const int NN = 2e5 + 10;
const int mod = 1e9 + 7;
struct node 
{
    int x, y;
    bool operator < (const node b) const 
    {
        if(x == b.x)
            return y < b.y;
        return x < b.x;
    }
}pp[N];
ll fac[NN], inv[NN];
void init()
{
    inv[0] = fac[0] = 1;
    for(int i = 1; i < NN; i ++)
        fac[i] = fac[i - 1] * i % mod;
    inv[NN - 1] = fast_pow(fac[NN - 1], mod - 2, mod);
    for(int i = NN - 2; i >= 1; i --)
        inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int n, int m)
{ 
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
ll dp[N];
int main()
{
    ios::sync_with_stdio(false);
    init();
    int h, w, n;
    cin >> h >> w >> n;
    for(int i = 0; i < n; i ++)
        cin >> pp[i].x >> pp[i].y;
    pp[n].x = h;
    pp[n].y = w;
    n ++;
    sort(pp, pp + n);
    dp[0] = C(pp[0].x + pp[0].y - 2, pp[0].x - 1);
    for(int i = 1; i <= n; i ++)
    {
        dp[i] = C(pp[i].x + pp[i].y -2, pp[i].x - 1);
        for(int j = 0; j < i; j ++)
        {
            if(pp[i].x >= pp[j].x && pp[i].y >= pp[j].y)//只能向上,向下或者斜上
                dp[i] = (dp[i] - dp[j] * C(pp[i].x - pp[j].x + pp[i].y - pp[j].y, pp[i].x - pp[j].x) % mod + mod) % mod;
        }
    }
    cout << dp[n - 1] << endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值