cf559c

组合数学DP

#include <cstdio>
#include <algorithm>
using namespace std;

#define d(x)
#define LL long long

const int MAX_N = 2020;
const int MAX_M = (int)(2e5) + 20;
const int MOD = (int)(1e9) + 7;

int row_num, col_num;
int n;
pair<int, int> point[MAX_N];
long long dp[MAX_N];

void input()
{
    scanf("%d%d%d", &row_num, &col_num, &n);
    for (int i = 0; i < n; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        point[i] = make_pair(a, b);
    }
    point[n++] = make_pair(row_num, col_num);
    point[n++] = make_pair(1, 1);
    for (int i = 0; i < n; i++)
    {
        point[i].first--;
        point[i].second--;
    }
}

LL multi_mod(LL a, LL b, LL c)
{    //返回(a*b) mod c,a,b,c<2^63
    a %= c;
    b %= c;
    LL ret = 0;
    while (b)
    {
        if (b & 1)
            ret = (ret + a) % c;
        a <<= 1;
        a %= c;
        b >>= 1;
    }
    return ret;
}

LL pow_mod(LL x, LL n, LL mod)
{  //返回x^n mod c ,非递归版
    x %= mod;
    if (n == 1)
        return x;
    LL ret = 1;
    while (n)
    {
        if (n & 1)
            ret = multi_mod(ret, x, mod);
        n >>= 1;
        x = multi_mod(x, x, mod);
    }
    return ret;
}

long long get_inverse(long long a)
{
    return pow_mod(a, MOD - 2, MOD);
}

long long factorial[MAX_M];
long long inverse[MAX_M];

void init_comb(int n)
{
    factorial[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        factorial[i] = factorial[i - 1] * i;
        factorial[i] %= MOD;
    }
    inverse[n] = get_inverse(factorial[n]);
    for (int i = n - 1; i >= 0; i--)
    {
        inverse[i] = inverse[i + 1] * (i + 1);
        inverse[i] %= MOD;
    }
}

long long comb(long long a, long long b)
{
    long long ret = factorial[a] * inverse[a - b];
    ret %= MOD;
    ret *= inverse[b];
    ret %= MOD;
    return ret;
}

int main()
{
    init_comb(200000);
    input();
    sort(point, point + n);
    dp[0] = 1;
    for (int i = 1; i < n; i++)
    {
        dp[i] = comb(point[i].first + point[i].second, point[i].second);
        for (int j = 1; j < i; j++)
        {
            if (point[j].first <= point[i].first && point[j].second <= point[i].second)
            {
                long long a = point[i].first - point[j].first;
                a += point[i].second - point[j].second;

                long long b = point[i].second - point[j].second;

                dp[i] -= (dp[j] * comb(a, b)) % MOD;
                dp[i] += MOD;
                dp[i] %= MOD;
            }
        }
        d(printf("dp[%d] = %d\n", i, (int)dp[i]));
    }
    printf("%d\n", (int)dp[n - 1]);
    return 0;
}

时间: 2024-10-12 05:55:43

cf559c的相关文章

CF559C Gerald and Giant Chess

题意 C. Gerald and Giant Chess time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Giant chess is quite common in Geraldion. We will not delve into the rules of the game, we'll just say that the

2016多校第六场1001-1003(hdu5793&amp;hdu5794&amp;hdu5795)

这场就做出一道题,怎么会有窝这么辣鸡的人呢? 1001 很复杂的公式,打表找的规律,最后是m^0+m^1+...+m^n,题解直接是(m^(n+1)-1)/(m-1),长姿势,原来还能化简……做出来的题不写题解了. 1002 一个棋盘,走棋的姿势要满足(x1-x2)^2+(y1-y2)^2==5,也就是以“日”字走,且只能向右下走. 其中有一些障碍不能经过,注意障碍有可能在终点,求从(1,1)走到(n,m)的路径数. 容斥+组合数 这题的简单版CF559C(对啊,我做过这题,我还是没做出来,wa

Gerald and Giant Chess

(Gerald and Giant Chess )[https://www.luogu.org/problemnew/show/CF559C] 给你一个\(n\times m\)的网格图,有t个棋子被标记成黑色,第i个黑色格子记做\((x_i,y_i)\),其余白色,现在询问在只能向下走或向右走的前提下,从\((1,1)\)到\((n,m)\)的不经过黑色格子的路径条数,\(n,m\leq 10^5,p\leq 2000\). 解 显然不会以传统做法,在哪一个格子为状态,突破点在只有2000个黑

【题解/总结】两双手(格路问题)/格路问题的某一本质

[题解]两双手(格路问题) 题目大意:求从\((0,0)\)到\((Ex,Ey)\)不经过给定障碍点的方案数.你每次移动只能是加上向量\(e_1\)或者向量\(e_2\),\(e_1,e_2\)中的基底都是整数. 考虑转化一下这个问题,从某个点走到在他右上角的某点需要加上\(ae_1+be_2\),这样我们就可以解出\(a,b\).我们把\((a,b)\)拿出来建立新的坐标系,就变成了简单的格路问题了.结合[题解]CF559C C. Gerald and Giant Chess(容斥+格路问题)