题意:
一匹”马”在棋盘上 (1,1) 的位置,每次跳跃时横纵坐标都必须增大.棋盘上还有 K 个障碍物(保证不在 (1,1) 处).求跳到 (n,m) 的方案数, .
题解:
马的日字步不好处理,需要先通过一种略奇妙的坐标变换,把问题转换成一步只能向右和向下.如果下标都改为从 1 开始(即读入的下标减 1 ),那么很容易推得变换后的坐标为 x′=−13x+23y,y′=23x−13y. 如果变换后的坐标不是非负整数,就说明这个点原本就无法到达.
这样转化之后的好处是如果没有障碍物的限制,从一个矩形左上角到达右下角的方案数可以用组合数方便地计算出来.所以我们考虑用总方案数减去经过至少一个障碍物的方案数.
为了避免重复计算,可以枚举第一个经过的障碍物,这样又转化为求从起点到这个障碍物,途中不经过其他障碍物的方案数以及从这个障碍物没有限制地到达终点的方案数.后者依旧用组合数,而前者是和原问题相同的一个子问题,又可以用同样的方法求.
于是可以设计一个dp算法, dp[i] 就表示从起点到障碍物 i ,途中不经过其他障碍物的方案数.这样dp的复杂度是 O(K2) 的.
当然,求组合数需要用到Lucas定理.
于是总的复杂度为 O(K2logP(n+m)) .
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 10010
#define MOD 1000000007
struct point
{
long long x,y;
bool operator <(const point &a)const
{
return x < a.x || (x == a.x && y < a.y);
}
} a[210];
long long n,m;
int r;
long long dp[110];
long long fac[1000010];
long long PowMod(long long a,long long b,long long mod)
{
long long ret = 1;
while(b)
{
if(b & 1)
ret = (ret * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ret;
}
long long Get_Fact(long long p)
{
fac[0] = 1;
for(long long i = 1; i <= p; i++)
fac[i] = (fac[i-1] * i) % p;
}
long long Lucas(long long n,long long m,long long p)
{
long long ret = 1;
while(n && m)
{
long long a = n % p;
long long b = m % p;
if(a < b)
return 0;
ret = (ret * fac[a] * PowMod(fac[b]*fac[a-b]%p,p-2,p)) % p;
n /= p;
m /= p;
}
return ret;
}
long long solve(int i,int j,long long mod)
{
long long x1 = a[i].x;
long long y1 = a[i].y;
long long x2 = a[j].x;
long long y2 = a[j].y;
long long c1 = (2*x2-y2-2*x1+y1) / 3;
long long c2 = (2*y2-x2-2*y1+x1) / 3;
if(2*x2-y2-2*x1+y1 < 0 || 2*y2-x2-2*y1+x1 < 0 || (2*x2-y2-2*x1+y1)%3 || (2*y2-x2-2*y1+x1)%3)
return 0;
return Lucas(c2+c1,c1,mod);
}
int ok(int i,int j)
{
if(a[i].x <= a[j].x && a[i].y <= a[j].y)
return 1;
return 0;
}
int main()
{
int t,C = 1;
//scanf("%d",&t);
long long mod = 110119;
Get_Fact(mod);
while(scanf("%lld%lld%d",&n,&m,&r) != EOF)
{
for(int i = 1; i <= r; i++)
scanf("%lld%lld",&a[i].x,&a[i].y);
a[0].x = 1;
a[0].y = 1;
a[r+1].x = n;
a[r+1].y = m;
sort(a,a+r+2);
memset(dp,0,sizeof(dp));
for(int i = 1; i <= r+1; i++)
{
dp[i] = solve(0,i,mod);
for(int j = i-1; j > 0; j--)
if(ok(j,i))
dp[i] = (dp[i] - dp[j]*solve(j,i,mod)%mod + mod)%mod;
}
printf("Case #%d: %lld\n",C++,dp[r+1]);
}
return 0;
}