概述
A Simple Chess
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1336 Accepted Submission(s): 358
(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.
For each test case:
The first line is three integers, n,m,r,(1≤n,m≤1018,0≤r≤100) , 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(1≤x≤n,1≤y≤m) , denoting the position of the obstacles. please note there aren't never a obstacles at position (1,1) .
1 1 0 3 3 0 4 4 1 2 1 4 4 1 3 2 7 10 2 1 2 7 1
Case #1: 1 Case #2: 0 Case #3: 2 Case #4: 1 Case #5: 5
题意
有个很大的棋盘,然后棋子可以走马步(横走1竖走2或横走2竖走1)
然后给一个子从1,1走到n,m
只能往格子坐标增大的方向跳
棋盘中有一些障碍物
问方案数。
解题思路
如果棋子走1,1应该是都见过的情况
组合数学直接求C(n+m,n)即可
其实变成1,2也没区别,只是此时存在了不能走的情况
想想每次走一步对于两个方向的总贡献是3
因此至少两个格点的坐标差值dx,dy的和是3的倍数才行
而步数就是st=(dx+dy)/3
那么每个方向上走2格的次数就分别是
nx=x-st,和ny=y-st
因为不能走回头路,因此这两个数值要大于零
然后就又变成了组合数学问题
nx+ny步中选nx步横向走2
于是方案数就是C(nx+ny,nx)
由于数字很大,模的数比较小,这里用Lucas定理处理大组合数取模的问题
接下来考虑解决路径上的障碍物了
可行方案数=总方案数-路过了至少一个障碍的方案数
由于障碍物的数量有100之多
裸容斥显然是会爆炸的
但是由于棋子走是有方向性的
如从左下到右上可走
则只有更左下的点到更右上的点才有重复
则可以按照左下到右上的方向排序。
这里考虑dp的方法
dp[I]表示起始点到i障碍不过任何障碍的路径数
因此dp[i]只要算出起点到i点的所有路径数,再减掉前i-1个点自己的路径数*(i-1中某个点到I点路径数)
即可。
接下来路过至少一个障碍的方案数=
sigma(dp[i]*i点到n,m点的路径数)
为什么呢?想想dp[1]*1到终点路径数,算出了哪些?
过1点的所有路径。
dp[2]*2到终点路径数呢?不过1点过2点的所有路径数
dp[3]*3到终点路径数呢?不过1点不过2点过3点的所有路径数
因此整个计数过程完备且没有重复,即为所求。
至此问题得解
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int M=105;
const int MO=110119;
//以下lucas定理
LL qp(LL a,LL n,LL mo)
{
LL ans=1;
while(n)
{
if(n&1)
ans=(ans*a)%mo;
a=(a*a)%mo;
n>>=1;
}
return ans;
}
struct Lucas
{
LL p;
LL fa[MO],inv[MO];//阶乘和逆元,注意空间大小
void init(LL _p)
{
p=_p;
fa[0]=1;
for(int i=1;i<p;i++)
{
fa[i]=fa[i-1]*i%p;
//cout<<"i "<<i;
inv[i]=qp(i,p-2,p);
//cout<<"i "<<i;
}
}
LL c(LL n,LL m)//小组合数取模
{
if(n==m||m==0)
return 1;
if(m>n)
return 0;
return fa[n]*inv[fa[n-m]*fa[m]%p]%p;
}
LL lucas(LL n,LL m)//大组合数取模
{
if(n==m||m==0)
return 1;
if(m>n)
return 0;
return c(n%p,m%p)*lucas(n/p,m/p)%p;
}
}lu;
struct Ob //障碍物
{
LL x,y;
bool operator<(const Ob &a)const
{
return x+y<a.x+a.y;
}
bool operator==(const Ob &a)const
{
return a.x==x&&a.y==y;
}
}ob[M];
bool check(LL dx,LL dy,LL &sx,LL &sy)//位置的差值,走的步数,返回能不能走
{
if((dx+dy)%3!=0)
return 0;
LL step=(dx+dy)/3;
sx=dx-step;//走2的数量
sy=dy-step;//走1的数量
if(sx<0||sy<0)
return 0;
return 1;
}
LL dp[M];
LL n,m;
int r;//障碍物数量
LL ans;
void solve()
{
LL sx,sy;
ans=0;
memset(dp,0,sizeof(dp));
if(check(n-1,m-1,sx,sy))
ans=lu.lucas(sx+sy,sx);
else
return;
for(int i=0;i<r;i++) //从开始到某障碍物不经过其他障碍物的路径数
{
if(!check(ob[i].x-1,ob[i].y-1,sx,sy))
continue;
dp[i]=lu.lucas(sx+sy,sx);
for(int j=0;j<i;j++)
if(check(ob[i].x-ob[j].x,ob[i].y-ob[j].y,sx,sy))
dp[i]=(dp[i]-dp[j]*lu.lucas(sx+sy,sx)%MO+MO)%MO;
}
for(int i=0;i<r;i++)
if(check(n-ob[i].x,m-ob[i].y,sx,sy))
ans=(ans-dp[i]*lu.lucas(sx+sy,sx)%MO+MO)%MO;
}
int main()
{
lu.init(MO);
int ca=1;
//cout<<"!!"<<endl;
while(scanf("%I64d%I64d%d",&n,&m,&r)!=EOF)
{
for(int i=0;i<r;i++)
scanf("%I64d%I64d",&ob[i].x,&ob[i].y);
sort(ob,ob+r);
r=unique(ob,ob+r)-ob;
solve();
printf("Case #%d: %I64dn",ca++,ans);
}
return 0;
}
最后
以上就是洁净泥猴桃为你收集整理的HDU_5794_ASimpleChess(Lucas定理&&(容斥||dp))A Simple Chess的全部内容,希望文章能够帮你解决HDU_5794_ASimpleChess(Lucas定理&&(容斥||dp))A Simple Chess所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复