2016多校联赛6B (hdu5794) A Simple Chess

本文探讨了一款名为ASimpleChess的游戏问题,目标是从棋盘左上角(1,1)位置移动到右下角(n,m)位置,且避开障碍物。文中详细介绍了如何使用组合数计算可行路径数量,并通过Lucas定理处理大数取模问题。

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


A Simple Chess

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


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
 

Recommend
wange2014   |   We have carefully selected several similar problems for you:   5803  5802  5801  5799  5798 
题意:给你一个n*m的棋盘,求从(1,1)走到(n,m)有几种方法。再给你障碍的位置,障碍是不可以停留的。

思路:很明显,大部分的人都能想出用组合数求方法(慢慢化简),那么这道题剩下的问题就是障碍的处理方法和大组合数取模的方法。大组合数取模我们用lucas定理,其实赛后我补这道题才知道有这玩意。原理我也没深究,直接找别人模板套了。。那么剩下的问题就是障碍怎么处理,因为一条路可能会经过多个障碍,所以这里比较麻烦。我们可以这样,先求出从起点到各个障碍各有多少种方法(我们设变量为value)。然后我们对障碍点排序,距离起始点近的排在前面,然后用循环去枚举两两障碍点,j为i后面的点,假设第i个障碍能到达第j个障碍点,那么p[j].value-=起始点经过i点到达j点的方法数(也就是p[i].value*i点到j点的方法数)。每次处理完所有i后面的点我们再用总方法数(起始点到终点的方法数)-起始点经过i点到达终点的方法数(同上不解释了),这样就不会重复计算了。下面给代码。

#include<iostream>   
#include<cstring>  
#include<cstdio> 
#include<algorithm>
#include<string>
#include<queue>
#include<cmath>
#include<set>
using namespace std;
#define maxn 150000
#define MOD 110119
typedef long long LL;
LL n, m, fac[maxn];
int r;
struct node{
	LL x, y, value;
}p[105];
LL quitmod(LL a, LL b)
{
	LL tmp = a % MOD, ans = 1;
	while (b)
	{
		if (b & 1)  ans = ans * tmp % MOD;
		tmp = tmp*tmp % MOD;
		b >>= 1;
	}
	return  ans;
}
LL C(LL n, LL m)
{
	if (m>n)return 0;
	return  fac[n] * quitmod(fac[m] * fac[n - m], MOD - 2) % MOD;
}
LL cfun(LL n, LL m)
{
	if (m == 0)  return 1;
	else return  (C(n%MOD, m%MOD)*cfun(n / MOD, m / MOD)) % MOD;
}
bool jud(node a, node b){
	if ((b.x + b.y - a.x - a.y) % 3 || 2 * (b.x - a.x) - (b.y - a.y)<0 || 2 * (b.y - a.y) - (b.x - a.x)<0)
		return false;
	return true;
}
bool cmp(node a, node b){
	return a.x + a.y < b.x + b.y;
}
LL getvalue(node a, node b){
	LL nn = (b.x + b.y - a.x - a.y) / 3;
	LL mm = b.x - a.x - nn;
	return a.value*cfun(nn, mm) % MOD;
}
int main(){
	fac[0] = 1;
	for (int i = 1; i < MOD; i++)
		fac[i] = fac[i - 1] * i%MOD;
	int tcase = 1;
	while (scanf("%lld%lld%d", &n, &m, &r) != EOF){
		int length = 1;
		p[0].x = 1;
		p[0].y = 1;
		p[0].value = 1;
		while (r--){
			scanf("%lld%lld", &p[length].x, &p[length].y);
			if (jud(p[0], p[length])){
				p[length].value = getvalue(p[0], p[length]);
				length++;
			}
		}
		sort(p, p + length, cmp);
		p[length].x = n;
		p[length].y = m;
		if (jud(p[0], p[length])){
			p[length].value = getvalue(p[0], p[length]);
		}
		else{
			printf("Case #%d: 0\n", tcase++);
			continue;
		}
		for (int i = 1; i < length; i++){
			for (int j = i + 1; j < length; j++){
				if (jud(p[i], p[j])){
					p[j].value -= getvalue(p[i], p[j]);
					p[j].value = (p[j].value + MOD) % MOD;
				}
			}
			if (jud(p[i], p[length])){
				p[length].value -= getvalue(p[i], p[length]) % MOD;
				p[length].value = (p[length].value + MOD) % MOD;
			}
		}
		printf("Case #%d: %lld\n", tcase++, p[length].value);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值