[JZOJ5612]【NOI2018模拟3.29】第三题(move)

Description

你在二维平面上行走,要从(0,0)走到(n,m)
你有三种走法,假设你当前在(x,y),你可以走到(x+1,y+1),(x+1,y-1)或者(x+2,y)

求方案数模100003
n,m<=1e18

Solution

将坐标系逆时针旋转45度
原来的问题就变成

在二维平面上行走,要从 (0,0) 走到 (n+m2,nm2)
你有三种走法,假设你当前在(x,y),你可以走到(x,y+1),(x+1,y)或者(x+1,y+1)(向上,向右或者向右上方斜着走)

求方案数

A=n+m2,B=nm2
显然若n,m奇偶性不同直接输出0, n<m 也输出0
比较正常的思路是枚举斜着走了i步
那么总共走了A+B-i步

因为 A>B ,所以答案就是 i=0ACiACAA+Bi (总共向右了A次,其中i次是斜着的,总共走了A+B-i步,其中A步是有向右的)
把两个A换成B也是一样的

又根据lucas定理,在模数进制下每一位对应组合数再乘起来
这样就可以数位DP了(虽然只有三四位的样子。。。),注意还要多一维0/1表示减法是否退位

还有一种非常棒的结论
我们考虑将一步斜向右上看做先向右再向上两步的一个合并

那么向右走与向上走的匹配数是 CiACiB

此时的i相当于枚举路径中有多少个从右到上的转折
然后一个转折可以变成斜着的,也可以不变成斜着的,因此乘上 2i
我们发现现在路径就是唯一确定了,因为向右和向上必定是连续的段,否则就会产生新的转折。

那么总答案就是 i=0B2iCiACiB

此时用lucas定理,就不需要数位DP了,同时发现 2i 我们将它也表示成模数进制,根据费马小定理,质数对模数-1取模,我们发现刚好就是它的在模数进制下每一位

直接对每一位计算,最后乘起来就行了

这个数有一个学名叫Delannoy Number
参见Wikipedia - Delannoy number

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
#define mo 100003
#define M 2005
#define N 400005
using namespace std;
LL n,m,js[N],ny[N],l,r;
int f[M][2*M];
LL ksm(LL k,LL n)
{
    LL s=1;
    for(;n;k=k*k%mo,n>>=1) if(n&1) s=s*k%mo;
    return s;
}
LL C(LL n,LL m)
{
    if(n<m) return 0;
    if(n>=mo) return C(n%mo,m%mo)*C(n/mo,m/mo)%mo;
    return js[n]*ny[m]%mo*ny[n-m]%mo;
}
int main()
{
    cin>>n>>m;
    if((n+m)%2!=0||n<m) printf("0\n");
    else if(n==m) printf("1\n");
    else
    {
        js[0]=1;
        LL ans=1;
        fo(i,1,mo-1) js[i]=js[i-1]*(LL)i%mo;
        ny[mo-1]=ksm(js[mo-1],mo-2);
        fod(i,mo-2,0) ny[i]=ny[i+1]*(LL)(i+1)%mo;
        l=(n-m)/2,r=(n+m)/2;
        while(l>0)
        {
            LL l1=l%mo,r1=r%mo,s=0;
            l/=mo,r/=mo;
            fo(i,0,min(l1,r1)) (s+=C(l1,i)*C(r1,i)%mo*ksm(2,i))%=mo;
            ans=ans*s%mo;
        }   
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值