Description
一个n*m棋盘,上面有r个黑块,初始马在(1,1)处,每次只能往右下跳,且不能跳到黑块处,问马跳到(n,m)的所有合法方案数
Input
多组用例,每组用例首先输入三个整数n,m,r分别表示棋盘规模以及黑块数量,之后r行每行两个整数x和y表示该黑块坐标,保证起点不是黑块,以文件尾结束输入
(1<=n,m<=10^18,0<=r<=100,1<=x<=n,1<=y<=m)
Output
对于每组用例,输出马从(1,1)跳到(n,m)的所有合法方案数,结果模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
Solution
因为此题n和m数据量过大,搜索显然不行,因黑块最多100个,所以考虑以黑块为突破点利用dp求解路径条数,设dp[i]为起点到第i个黑块的合法路径数,num[i][j]为第i个黑块到第j个黑块的路径数,设起点为第0个黑块,终点为第n+1和黑块,那么有以下转移方程dp[i]=num[0][i]-sum{ dp[j]*num[i][j] },j=1,2,…,i-1,dp[n+1]即为答案
剩余一个问题没有解决的是从(x1,y1)跳到(x2,y2)的路径数,令x=x2-x1,y=y2-y1,设马跳横日a次,竖日b次,则有2a+b=x,2b+a=y,解得a=(2x-y)/3,b=(2y-x)/3,那么路径数就是C(a+b,a),此处a和b都到了10^18级别,故要用Lucas定理求组合数
Code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 111111
#define mod 110119
ll dp[111];
struct node
{
ll x,y;
bool operator <(const node &b)const
{
if(x!=b.x)return x<b.x;
return y<b.y;
}
}p[111];
ll mod_pow(ll a,ll b,ll p)//快速幂
{
ll ans=1;
a%=p;
while(b)
{
if(b&1)ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}
ll rev[maxn],power[maxn];
void init(ll n,ll p)//预处理出n的阶乘及其阶乘的逆元
{
power[0]=1;
for(int i=1;i<n;i++)
power[i]=power[i-1]*i%p;
rev[1]=1;
for(ll i=2;i<n;i++)
rev[i]=mod_pow(power[i],p-2,p);
}
ll C(ll n,ll m,ll p)//求组合数
{
if(m>n||m<0||n<0)return 0;
if(m==0||m==n) return 1;
return power[n]*rev[m]%p*rev[n-m]%p;
}
//n,m较大时需要用到lucas定理
ll lucas(ll n,ll m,ll p)
{
ll ans=1;
while(n&&m)
{
ll a=n%p,b=m%p;
if(a<b) return 0;
ans=ans*C(a,b,p)%p;
n/=p;
m/=p;
}
return ans;
}
ll solve(ll x1,ll y1,ll x2,ll y2)//求(x1,y1)到(x2,y2)的所有路径数
{
ll x=x2-x1,y=y2-y1;
if((2ll*x-y)%3!=0||(2ll*y-x)%3!=0)return 0;
ll a=(2ll*x-y)/3,b=(2ll*y-x)/3;
if(a<0||b<0)return 0;
return lucas(a+b,a,mod);
}
ll n,m;
int r,Case=1;
int main()
{
init(mod,mod);
while(~scanf("%I64d%I64d%d",&n,&m,&r))
{
for(int i=1;i<=r;i++)
scanf("%I64d%I64d",&p[i].x,&p[i].y);
r++;
p[r].x=n,p[r].y=m;
sort(p+1,p+r+1);
for(int i=1;i<=r;i++)//预处理出起点到达某黑块的所有路径数
dp[i]=solve(1,1,p[i].x,p[i].y);
for(int i=1;i<=r;i++)
for(int j=i+1;j<=r;j++)
{
if(p[j].y<p[i].y)continue;
dp[j]-=(dp[i]*solve(p[i].x,p[i].y,p[j].x,p[j].y))%mod;
dp[j]=(dp[j]%mod+mod)%mod;
}
printf("Case #%d: %I64d\n",Case++,dp[r]);
}
return 0;
}