【BJWC2018】上学路线(dp,Lucas,crt)

考虑 dp。

我们先把 ( n , m ) (n,m) (n,m) 也当做障碍点,然后把所有的障碍点按 x x x 坐标为第一关键字, y y y 坐标为第二关键字排序。

然后设 f i f_i fi 表示到达第 i i i 个障碍点的合法总方案数(途中不经过障碍点)。显然,答案就是 f t + 1 f_{t+1} ft+1,也就是到达 ( n , m ) (n,m) (n,m) 的总方案数。

至于为什么要先排序,是因为我们要保证当处理 f i f_i fi 时,能转移到 f i f_i fi 的所有 f j f_j fj 都已经处理完了。

显然有状态转移方程:(其中 x i x_i xi 表示第 i i i 个障碍点的 x x x 坐标, y i y_i yi 同理)

f i = ( x i + y i x i ) − ∑ j = 1 i − 1 f j × ( ( x i + y i ) − ( x j + y j ) x i − x j ) f_i=inom{x_i+y_i}{x_i}-sum_{j=1}^{i-1}f_j imesinom{(x_i+y_i)-(x_j+y_j)}{x_i-x_j} fi=(xixi+yi)j=1i1fj×(xixj(xi+yi)(xj+yj))

首先, ( x i + y i x i ) dbinom{x_i+y_i}{x_i} (xixi+yi) 是从 ( 0 , 0 ) (0,0) (0,0) ( x i , y i ) (x_i,y_i) (xi,yi) 的总方案(不论是否合法)。

证明:从 ( 0 , 0 ) (0,0) (0,0) ( x i , y i ) (x_i,y_i) (xi,yi) 一共需要 x i + y i x_i+y_i xi+yi 步,其中需要横着走 x i x_i xi 步,竖着走 y i y_i yi 步,那么我们就枚举在哪几步横着走,也就是 ( x i + y i x i ) dbinom{x_i+y_i}{x_i} (xixi+yi)

然后 ( ( x i + y i ) − ( x j + y j ) x i − x j ) dbinom{(x_i+y_i)-(x_j+y_j)}{x_i-x_j} (xixj(xi+yi)(xj+yj)) 是从 ( x j , y j ) (x_j,y_j) (xj,yj) ( x i , y i ) (x_i,y_i) (xi,yi) 的总方案(不论是否合法),证明同理。

然后证明那些不合法的方案就是 ∑ j = 1 i − 1 f j × ( ( x i + y i ) − ( x j + y j ) x i − x j ) sum_{j=1}^{i-1}f_j imesdbinom{(x_i+y_i)-(x_j+y_j)}{x_i-x_j} j=1i1fj×(xixj(xi+yi)(xj+yj))

设某一条不合法路径上的第一个障碍点是第 f i r s t first first 个障碍点。

那么 f j × ( ( x i + y i ) − ( x j + y j ) x i − x j ) f_j imesdbinom{(x_i+y_i)-(x_j+y_j)}{x_i-x_j} fj×(xixj(xi+yi)(xj+yj)) 表示的就是那些 f i r s t = j first=j first=j 的不合法路径,因为这种路径从 ( 0 , 0 ) (0,0) (0,0) ( x j , y j ) (x_j,y_j) (xj,yj) 是没有障碍点的,正好对应 f j f_j fj

这么解释的话,就容易证明 ∑ j = 1 i − 1 f j × ( ( x i + y i ) − ( x j + y j ) x i − x j ) sum_{j=1}^{i-1}f_j imesdbinom{(x_i+y_i)-(x_j+y_j)}{x_i-x_j} j=1i1fj×(xixj(xi+yi)(xj+yj)) 和所有的不合法路径是一一对应的了。

那么这个状态转移方程就是正确的了。

现在的问题就是 ( n m )   m o d   P dbinom{n}{m}mod P (mn)modP 应该怎么求。

显然,当 P = 1000003 ∈ p r i m e P=1000003in prime P=1000003prime 时,这个可以用 Lucas 定理直接算。

但是当 P = 1019663265 = 3 × 5 × 6793 × 10007 P=1019663265=3 imes5 imes6793 imes10007 P=1019663265=3×5×6793×10007 时,就不能直接用 Lucas 算了。

这个需要用中国剩余定理来做,不会的请先去做一下 【模板】中国剩余定理(CRT)/曹冲养猪

中国剩余定理的结论大概是这个东西:

对于一个方程组:

{ x ≡ a 1 ( m o d p 1 ) x ≡ a 2 ( m o d p 2 ) ⋯ x ≡ a k ( m o d p k ) egin{cases} xequiv a_1 pmod {p_1}\ xequiv a_2 pmod {p_2}\ cdots\ xequiv a_k pmod {p_k} end{cases} xa1(modp1)xa2(modp2)xak(modpk)

其中 p 1 , p 2 , … , p k p_1,p_2,dots,p_k p1,p2,,pk 两两互质。

M = ∏ i = 1 k p i M=prodlimits_{i=1}^{k}p_i M=i=1kpi m i = M p i m_i=dfrac{M}{p_i} mi=piM t i t_i ti m i m_i mi p i p_i pi 意义下的逆元。那么 x x x 的一个特殊解就是 x 0 = ∑ i = 1 k a i m i t i x_0=sumlimits_{i=1}^{k}a_im_it_i x0=i=1kaimiti

然后又因为 x x x 一定能表示成 a × M + b a imes M+b a×M+b 的形式,所以 x x x 的最小非负数解就是 x 0   m o d   M x_0mod M x0modM

这个题中,未知数 x x x 就是 ( n m ) dbinom{n}{m} (mn),我们要求 x   m o d   P xmod P xmodP,然后我们设 p 1 = 3 p_1=3 p1=3 p 2 = 5 p_2=5 p2=5 p 3 = 6793 p_3=6793 p3=6793 p 4 = 10007 p_4=10007 p4=10007,那么 M M M 就是这题中的 P P P

然后我们还知道 a 1 = x   m o d   p 1 a_1=xmod p_1 a1=xmodp1 a 2 = x   m o d   p 2 a_2=xmod p_2 a2=xmodp2 a 3 = x   m o d   p 3 a_3=xmod p_3 a3=xmodp3 a 4 = x   m o d   p 4 a_4=xmod p_4 a4=xmodp4 的值(这些都可以用 Lucas 定理求出来)。

那么我们通过中国剩余定理就可以把 x   m o d   P xmod P xmodP 算出来了。

具体代码如下:

#include<bits/stdc++.h>

#define T 210
#define ll long long

using namespace std;

struct Point
{
	int x,y;
	Point(){};
	Point(int a,int b){x=a,y=b;}
}q[T];

int n,m,num,P;
ll f[T];

ll poww(ll a,ll b,ll p)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}

namespace mod1//p=1019663265的情况
{
	const ll M=1019663265ll,p[5]={0,3,5,6793,10007};
	ll m[5],t[5],fac[5][10010],inv[5][10010];
	ll a[5];
	void exgcd(ll a,ll b,ll &x,ll &y)
	{
		if(!b)
		{
			x=1,y=0;
			return;
		}
		exgcd(b,a%b,x,y);
		ll tmp=x;
		x=y;
		y=tmp-a/b*y;
	}
	void init()
	{
		for(int i=1;i<=4;i++)
		{
			m[i]=M/p[i];
			ll x,y;
			exgcd(m[i],p[i],x,y);//通过exgcd求逆元
			t[i]=(x%p[i]+p[i])%p[i];
		}
		for(int i=1;i<=4;i++)
		{
			fac[i][0]=1;
			for(int j=1;j<p[i];j++)
				fac[i][j]=fac[i][j-1]*j%p[i];
		}
		for(int i=1;i<=4;i++)
			for(int j=0;j<p[i];j++)
				inv[i][j]=poww(fac[i][j],p[i]-2,p[i]);
	}
	ll C(ll n,ll m,int k)
	{
		if(n<m) return 0;
		if(!m) return 1;
		return fac[k][n]*inv[k][n-m]%p[k]*inv[k][m]%p[k];
	}
	ll Lucas(ll n,ll m,int k)
	{
		if(n<m) return 0;
		if(!m) return 1;
		return C(n%p[k],m%p[k],k)*Lucas(n/p[k],m/p[k],k)%p[k];
	}
	ll solve(ll n,ll mm)
	{
		ll x=0;
		for(int i=1;i<=4;i++)
		{
			a[i]=Lucas(n,mm,i);
			x=(x+a[i]*m[i]%M*t[i]%M)%M;//求x的解
		}
		return x;
	}
}

namespace mod2//p=1000003的情况
{
	const ll p=1000003;
	ll fac[1000005],inv[1000005];
	void init()
	{
		fac[0]=1;
		for(int i=1;i<p;i++)
			fac[i]=fac[i-1]*i%p;
		for(int i=0;i<p;i++)
			inv[i]=poww(fac[i],p-2,p);
	}
	ll C(ll n,ll m)
	{
		if(n<m) return 0;
		if(!m) return 1;
		return fac[n]*inv[n-m]%p*inv[m]%p;
	}
	ll Lucas(ll n,ll m)
	{
		if(n<m) return 0;
		if(!m) return 1;
		return C(n%p,m%p)*Lucas(n/p,m/p)%p;
	}
}

bool cmp(Point a,Point b)
{
	if(a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&num,&P);
	if(P==1000003) mod2::init();
	else mod1::init();
	for(int i=1;i<=num;i++)
		scanf("%d%d",&q[i].x,&q[i].y);
	q[num+1]=Point(n,m);
	sort(q+1,q+num+2,cmp);
	for(int i=1;i<=num+1;i++)
	{
		if(P==1000003) f[i]=mod2::Lucas(q[i].x+q[i].y,q[i].x);
		else f[i]=mod1::solve(q[i].x+q[i].y,q[i].x);
		for(int j=1;j<i;j++)
		{
			if(q[j].x<=q[i].x&&q[j].y<=q[i].y)
			{
				if(P==1000003) f[i]=(f[i]-f[j]*(mod2::Lucas(q[i].x+q[i].y-q[j].x-q[j].y,q[i].x-q[j].x))%P+P)%P;
				else f[i]=(f[i]-f[j]*(mod1::solve(q[i].x+q[i].y-q[j].x-q[j].y,q[i].x-q[j].x))%P+P)%P;
			}
		}
	}
	printf("%lld
",f[num+1]);
	return 0;
}
原文地址:https://www.cnblogs.com/ez-lcw/p/14448653.html