【HDU 5794 A Simple Chess】题解

题目链接

题目

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 (x2−x1)2+(y2−y1)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.

\(N * M\) 的矩阵,从左上角到右下角的方案数。
其中:

  • 相邻两次的步长必须满足:\((y_2 - y_1)^2 + (x_2 - x_1)^2 = 5\ \&\&\ y2 > y1\ \&\&\ x2 > x1\)
  • \(K\) 个格子不能进入。

\(N,M <= 10^{9},\ K<=100\)

思路

我们发现棋盘很大,不可能一个个格子算,于是我们尝试对关键点(不能到的格子和终点)来算。

引理:

\(n\times m\) 的棋盘中,每次可以向下或向右走一格,从左上角到右下角的方案为:

\[\Large C_{n+m}^n=C_{n+m}^m \]

然而这道题中是走日子格,于是我们可以吧棋盘变一下,变成上面的经典问题。

假设当前位置在 \((i, j)\),则从起点要走 \((i+j-2)\) (这是当 \(i\neq 1 ,j\neq 1\) 时,不过数据好像没有卡,所以我代码也没写)个格子到这里,而日子格每次可以走3格,所以会走 \(\frac{(i+j-2)}{3}\) 步。每次走日子格都至少向下和向右一格,然后是向下或向右一格,所以 \((i, j)\) 就可以转化为 \((i-\frac{(i+j-2)}{3}, j-\frac{(i+j-2)}{3})\)。不合法的情况还需要特判一下。

\(dp_i\) 表示到达第 \(i\) 个关键点的合法方案数。然后我们套容斥枚举第一个不合法的点,假设是 \(dp_j\),然后再拿乘上从 \(j\) 走到 \(i\) 的方案数。

\[\Large dp_i=f(1, 1, x_i, y_i)-\sum_{j=1}^{i-1}(dp_j\times f(x_j, y_j, x_i, y_i)) \]

组合数可以用lacus定理来优化。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
#define p 110119
#define NN 120
struct node
{
	int x, y; 
}a[NN]; 
int n, m, i, j, k; 
int jc[p*3], dp[NN]; 
int f, ans, N, M, t; 

bool cmp(node x, node y)
{
	if(x.x==y.x) return x.y<y.y; 
	return x.x<y.x; 
}

int kuai(int a, int b)
{
	int ans=1;  
	while(b)
	{
		if(b&1) ans=(ans*a)%p; 
		b>>=1; 
		a=(a*a)%p; 
	}
	return ans; 
}

int C(int m, int n)
{
	if(m<n) return 0; 
	return jc[m]*kuai(jc[n]*jc[m-n]%p, p-2); 
}

int lacus(int m, int n)
{
	if(m==0) return 1; 
	return lacus(m/p, n/p)*C(m%p, n%p)%p; 
}

int check(int a, int b)
{
	if((a+b-2)%3) return 0;  
	// printf("-------\n"); 
	f=(a+b-2)/3; a-=f; b-=f; 
	if(a<=0||b<=0) return 0; 
	return 1; 
}

int count(int lx, int ly, int rx, int ry)
{
 	if(!check(lx, ly)) return 0; 
 	if(!check(rx, ry)) return 0; 
 	f=(lx+ly-2)/3; lx-=f; ly-=f; 
 	f=(rx+ry-2)/3; rx-=f; ry-=f; 
 	if(rx-lx<0) return 0; 
 	if(ry-ly<0) return 0; 
 	return lacus(rx-lx+ry-ly, rx-lx); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	for(i=jc[0]=1; i<=p; ++i) jc[i]=jc[i-1]*i%p; 
	while(scanf("%lld%lld%lld", &N, &M, &n)!=EOF)
	{
		printf("Case #%lld: ", ++t); 
		for(i=1; i<=n; ++i) a[i].x=read(), a[i].y=read(); 
		sort(a+1, a+n+1, cmp); 
		// printf("%lld %lld\n", N, M); 
		if(!check(N, M)) {printf("0\n"); continue; }
		for(i=1; i<=n; ++i)
		{
			dp[i]=count(1, 1, a[i].x, a[i].y); 
			for(j=1; j<i; ++j)
				dp[i]=((dp[i]-dp[j]*count(a[j].x, a[j].y, a[i].x, a[i].y)%p)%p+p)%p; 
			// printf("dp[%lld]=%lld\n", i, dp[i]); 
		}
		ans=count(1, 1, N, M); 
		for(i=1; i<=n; ++i)
			ans=((ans-dp[i]*count(a[i].x, a[i].y, N, M)%p)%p+p)%p; 
		printf("%lld\n", ans); 
	}
	return 0; 
}

posted @ 2021-12-07 22:33  zhangtingxi  阅读(26)  评论(0编辑  收藏  举报