HDU 5794 A Simple Chess Lucas大数组合数取模

博客介绍了如何利用Lucas大数组合数取模解决HDU 5794中一个简单的国际象棋问题。通过对组合数学和棋盘问题的结合分析,阐述了计算技巧在解决此类问题中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A Simple Chess

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


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
 
从起点(1,1)按日字形往终点(n,m)走,某些点有障碍不能走,问一共有多少种走法到达终点。
列一个二元一次方程,X代表需要走X个(1,2),Y代表需要走Y个(2,1),解出X与Y,C(x+y,x)代表总线路数。
因为n,m较大,需要用Lucas定理求大组合数取模。
然后R平方的复杂度解决容斥,对不能走的点排序,从前往后分析每一个障碍点影响的线路数目。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <bitset>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define maxn 100200
#define LL long long
using namespace std;
const LL p = 110119;
LL quick_mod(LL a, LL b)
{
        LL ans = 1;
        a %= p;
        while (b)
        {
                if (b & 1)
                {
                        ans = ans * a % p;
                        b--;
                }
                b >>= 1;
                a = a * a % p;
        }
        return ans;
}
LL num[p + 10];
void init()
{
        num[0] = 1ll;
        for (int i = 1; i <= p; i++)
        {
                num[i] = (num[i - 1] * i) % p;
        }
}
LL C(LL n, LL m)
{
        if (m > n)
        {
                return 0;
        }
        return (num[n] * quick_mod(num[m], p - 2) % p) * quick_mod(num[n - m], p - 2) % p;
}
LL Lucas(LL n, LL m)
{
        if (m == 0)
        {
                return 1;
        }
        return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
LL dis(LL a, LL b, LL n, LL m)
{
        if (a == n && b == m)
        {
                return 1;
        }
        if (n <= a || m <= b)
        {
                return 0;
        }
        if ((2 * n - m + b - 2 * a) % 3 != 0 || (2 * m - n + a - 2 * b) % 3 != 0)
        {
                return 0;
        }
        LL x = (2 * n - m + b - 2 * a) / 3;
        LL y = (2 * m - n + a - 2 * b) / 3;
        if (x < 0 || y < 0)
        {
                return 0;
        }
        LL sum = (m + n - a - b) / 3;
        return Lucas(sum, min(x, y));
}
struct point
{
        LL x;
        LL y;
        friend bool operator < (point a, point b)
        {
                if (a.x == b.x)
                {
                        return a.y < b.y;
                }
                return a.x < b.x;
        }
};
int main()
{
        LL n, m, r;
        int tt = 1;
        init();
        while (scanf("%I64d %I64d %I64d", &n, &m, &r) != EOF)
        {
                LL ans = dis(1, 1, n, m);
                point rs[110];
                int flag=0;
                for (int i = 0; i < r; i++)
                {
                        scanf("%I64d %I64d", &rs[i].x, &rs[i].y);
                        if(rs[i].x==n&&rs[i].y==m)
                        {
                                flag=1;
                                printf("Case #%d: 0\n", tt++);
                                break;
                        }
                }
                if(flag)
                {
                        continue;
                }
                sort(rs, rs + r);
                LL tmp[110];
                for (int i = 0; i < r; i++)
                {
                        tmp[i] = dis(1, 1, rs[i].x, rs[i].y);
                        for (int j = 0; j < i; j++)
                        {
                                tmp[i] = (tmp[i] - tmp[j] * dis(rs[j].x, rs[j].y, rs[i].x, rs[i].y) % p + p) % p;
                        }
                        ans = (ans - tmp[i] * dis(rs[i].x, rs[i].y, n, m) % p + p) % p;
                }
                printf("Case #%d: %lld\n", tt++, ans);
        }
        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值