一、题目
马走日,问禁止一些点的情况下起点到终点的方案数,模 110119 110119 110119
二、解法
先考虑没有限制两个点的情况,设 x x x方向需要移动 a a a, y y y方向需要移动 b b b,设 p = ( a + b ) / 3 p=(a+b)/3 p=(a+b)/3,所以方案数是 C ( p , a − p ) C(p,a-p) C(p,a−p),就是说每一步 x , y x,y x,y都会至少靠近 1 1 1,而 x x x还需要走额外 a − p a-p a−p步,就从 p p p步中选。
然后我们先把给出的禁止点排序,按照
x
+
y
x+y
x+y的和从大到小,因为它们一定满足一个拓扑序,然后还要考虑去重,设
d
p
[
i
]
dp[i]
dp[i]为到第
i
i
i个禁止点未经过任何前面的禁止点的方案数(所以我们需要把
(
n
,
m
)
(n,m)
(n,m)插入进去,方便统计答案),转移如下:
d
p
[
i
]
=
w
a
y
(
(
1
,
1
)
,
i
)
−
∑
j
=
1
i
−
1
d
p
[
j
]
×
w
a
y
(
j
,
i
)
dp[i]=way((1,1),i)-\sum_{j=1}^{i-1} dp[j]\times way(j,i)
dp[i]=way((1,1),i)−j=1∑i−1dp[j]×way(j,i)其中
w
a
y
way
way的方法上面已经讲过了,用
l
u
c
a
s
lucas
lucas算一算就行。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int M = 105;
const int MOD = 110119;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,dp[M],fac[MOD+5],inv[MOD+5];
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=2;i<n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=2;i<n;i++) inv[i]=inv[i]*inv[i-1]%MOD;
for(int i=1;i<n;i++) fac[i]=fac[i-1]*i%MOD;
}
int C(int n,int m)
{
if(m>n) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int lucas(int n,int m)
{
if(m==0) return 1;
return lucas(n/MOD,m/MOD)*C(n%MOD,m%MOD)%MOD;
}
struct node
{
int x,y;
node(int X=0,int Y=0) : x(X) , y(Y) {}
bool operator < (const node &b) const
{
return x+y<b.x+b.y;
}
int check(const node &t) const
{
if(x<=t.x || y<=t.y) return 0;
int a=x-t.x,b=y-t.y;
if((a+b)%3!=0 || a>2*b || b>2*a) return 0;
return 1;
}
int cnt(const node &t) const
{
int a=x-t.x,b=y-t.y;
int p=(a+b)/3;
return lucas(p,a-p);
}
}a[M];
signed main()
{
init(MOD);
int Case=0;
while(~scanf("%lld %lld %lld",&n,&m,&k))
{
memset(dp,0,sizeof dp);
int f=0;
for(int i=1;i<=k;i++)
{
a[i].x=read();
a[i].y=read();
if(a[i].x==n && a[i].y==m) f=1;
}
printf("Case #%d: ",++Case);
if(n==1 && m==1)
{
puts("1");
continue;
}
if(f==1)
{
puts("0");
continue;
}
sort(a+1,a+1+k);
a[++k].x=n;a[k].y=m;
for(int i=1;i<=k;i++)
{
if(!a[i].check(node(1,1))) continue;
dp[i]=a[i].cnt(node(1,1));
for(int j=1;j<k;j++)
{
if(!a[i].check(a[j])) continue;
dp[i]=(dp[i]-dp[j]*a[i].cnt(a[j]))%MOD;
}
}
printf("%d\n",(dp[k]+MOD)%MOD);
}
}