题意:
数据范围:
Analysis:
20分很显然的设
f
i
,
j
f_{i,j}
fi,jDP。
50分,观察这个三个式子。类似于每次走(x+1,y),(x,y+1),根据一个步数可以确定另一个,然后组合数算,是否能够类似的做。假设我们每个走了
x
,
y
,
z
x,y,z
x,y,z次。就有方程:
x
+
y
+
2
z
=
n
x+y+2z=n
x+y+2z=n
x
−
y
=
m
x-y=m
x−y=m
解得:
x
=
y
+
m
,
z
=
n
−
m
2
−
y
x=y+m,z=\frac{n-m}{2}-y
x=y+m,z=2n−m−y
也就是说对于一个确定的
y
y
y,其它也随之确定。显然可以用组合数算。
设:
A
=
n
−
m
2
,
B
=
n
+
m
2
A=\frac{n-m}{2},B=\frac{n+m}{2}
A=2n−m,B=2n+m
有:
∑
i
=
0
A
C
i
+
B
i
C
B
i
+
m
\sum_{i=0}^{A}C_{i+B}^{i}C_{B}^{i+m}
∑i=0ACi+BiCBi+m
100分:快速计算组合数,又有模数。套路的用
L
u
c
a
s
Lucas
Lucas定理,在
m
o
mo
mo进制下DP。因为有
i
+
m
i+m
i+m,要多设一维
0
/
1
0/1
0/1表示是否进位。
但还有一种更简单的做法:发现第三种走法等于走一次第一种,走一次第二种。
不妨假设只有第一,二种,合并出第三种。枚举多少个第一二种配对第三种,并且其可以变成第三种也可以不变成第三种,则有式子:
∑
i
=
0
A
2
i
C
A
i
C
B
i
\sum_{i=0}^{A}2^iC_{A}^iC_{B}^i
i=0∑A2iCAiCBi
这个直接逐位枚举
m
o
mo
mo进制下每一位的数算就好了。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 2e5 + 5;
const int mo = 100003;
typedef long long ll;
int inv[N],fac[N],fac_[N];
ll n,m;
inline int pow(int x,int p)
{
int ret = 1;
for (; p ; p >>= 1,x = (ll)x * x % mo)
if (p & 1) ret = (ll)ret * x % mo;
return ret;
}
inline int C(ll n,ll m)
{
if (n < m) return 0;
if (n >= mo) return (ll)C(n / mo,m / mo) * C(n % mo,m % mo) % mo;
return (ll)fac[n] * fac_[m] % mo * fac_[n - m] % mo;
}
int main()
{
freopen("move.in","r",stdin);
freopen("move.out","w",stdout);
scanf("%lld%lld",&n,&m);
if (n < m || (n + m) % 2) { puts("0"); return 0; }
if (n == m) { puts("1"); return 0; }
ll A = (n - m) / 2,B = (n + m) / 2; fac[0] = fac_[0] = inv[1] = 1;
for (int i = 1 ; i < mo ; ++i)
{
if (i > 1) inv[i] = (ll)(mo - mo / i) * inv[mo % i] % mo;
fac[i] = (ll)fac[i - 1] * i % mo;
fac_[i] = (ll)fac_[i - 1] * inv[i] % mo;
}
int ans = 1;
while (A > 0)
{
int l = A % mo,r = B % mo,now = 0;
A /= mo,B /= mo;
for (int i = 0 ; i <= min(l,r) ; ++i) now = (now + (ll)C(l,i) * C(r,i) % mo * pow(2,i) % mo) % mo;
ans = (ll)ans * now % mo;
}
printf("%d\n",ans);
return 0;
}