【HDOJ 5794】A Simple Chess(大组合数Lucas定理+容斥)

【HDOJ 5794】A Simple Chess(大组合数Lucas定理+容斥)

A Simple Chess

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1081    Accepted Submission(s): 291

Problem Description
There is a n×m board, a chess want to go to the position
(n,m) from the position (1,1) .
The chess is able to go to position (x2,y2) from the position (x1,y1) , only and if only x1,y1,x2,y2 is satisfied that (x2x1)2+(y2y1)2=5, x2>x1, y2>y1 .
Unfortunately, there are some obstacles on the board. And the chess never can stay on the grid where has a obstacle.
I want you to tell me, There are how may ways the chess can achieve its goal.

Input
The input consists of multiple test cases.
For each test case:
The first line is three integers, n,m,r,(1n,m1018,0r100) , denoting the height of the board, the weight of the board, and the number of the obstacles on the board.
Then follow r lines, each lines have two integers, x,y(1xn,1ym), denoting the position of the obstacles. please note there aren’t never a obstacles at position (1,1) .

Output
For each test case,output a single line “Case #x: y”, where x is the case number, starting from 1. And y is the answer after module 110119 .

Sample Input
1 1 0
3 3 0
4 4 1
2 1
4 4 1
3 2
7 10 2
1 2
7 1

Sample Output
Case #1: 1
Case #2: 0
Case #3: 2
Case #4: 1
Case #5: 5

Author
UESTC

Source
2016 Multi-University Training Contest 6

根据公式一推,很容易发现是按象棋中马来移动的。
不存在马脚的一说。
然后给出r个陷阱,要求 n×m 的图上,不能走到陷阱情况下从点 (1,1) 走到终点 (n,m) 的走法。

已知陷阱不会出现在起点 (1,1)

首先需要推得对于任意无陷阱的 n×m 的图中,走到终点 (n,m) 的走法。

打个表可以看出是杨辉三角,也就是组合数,推到 n×m 的图上就是 Cnn+m23n+m231)

但是n和m很大,就要用到Lucas,正经的Lucas我们发现会TLE
因为Lucas里的那个 Cnm 也是O(mod)的。因为模数很小,可以用最原始的公式 Cnm=m!n!(mn)! 这样可以预处理出来阶乘,因为要取模,逆元也一并用快速幂搞出来即可。

以上是Lucas的部分。
因为要去掉经过陷阱到达 (n,m) 的路径,这里用到一个小容斥。
定义 dp[i] 表示从 (1,1) 不经过陷阱到达第 i 个陷阱的路径数目。
预先对陷阱进行先行后列的二级排序。这样保证遍历到第i个陷阱时,从 (1,1) 出发到 i 陷阱过程中可能经过的陷阱之前都已经遍历过了。
dp[i]=(1,1)iji
这样最终 dp[i] 就表示从 (1,1) 不经过任何陷阱到达 i 陷阱的路径数。

可以把(n,m)也作为陷阱加进去,这样最后答案就是 dp[r] dp[0,1,..,r1] 存陷阱,r为终点)

注意对点 (n,m) 的可达性的判断,因为少了个判断导致对负数进行了组合数运算=。=RE半天

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<LL,LL>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 110119;
const double eps = 1e-8;

LL p[110120];
LL inv[110120];

LL pow_m(LL a,int b)
{
    LL ans = 1;
    while(b)
    {
        if(b&1) ans = (ans*a)%mod;
        b >>= 1;
        a = (a*a)%mod;
    }
    return ans;
}

void init()
{
    p[0] = 1;
    for(int i = 1; i < mod; ++i) p[i] = (i*p[i-1])%mod;
    for(int i = 0; i < mod; ++i) inv[i] = pow_m(p[i],mod-2);
}

LL C(int m,int n)
{
    if(n > m) return (LL)0;
    if(m == n) return (LL)1;
    return (((p[m]*inv[n])%mod)*inv[m-n])%mod;
}

LL Lucas(LL n,LL m)
{
    if(m == 0) return(LL)1;
    return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}

LL got(LL n,LL m)
{
    if(n <= 0 || m <= 0) return 0;
    LL mn,mx;
    mn = min(m,n);
    mx = max(m,n);
    LL v = (mn+mx-2)/3;
    if((mn+mx-2)%3 || v+1 > mn) return 0;
    return Lucas(v,mn-v-1);
}

Pr pr[111];
LL dp[111];

int main()
{
    //fread("");
    //fwrite("");

    LL n,m;
    int r,z = 1;
    init();

    while(~scanf("%lld%lld%d",&n,&m,&r))
    {
        for(int i = 0; i < r; ++i)
        {
            scanf("%lld%lld",&pr[i].first,&pr[i].second);
        }
        sort(pr,pr+r);
        pr[r].first = n;
        pr[r].second = m;

        for(int i = 0; i <= r; ++i)
        {
            dp[i] = got(pr[i].first,pr[i].second);
            for(int j = 0; j < i; ++j)
            {
                dp[i] = (((dp[i]-(dp[j]*got(pr[i].first-pr[j].first+1,pr[i].second-pr[j].second+1))%mod)%mod)+mod)%mod;
            }
        }

        printf("Case #%d: %lld\n",z++,dp[r]);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值