• 【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;
    }
    
  • 相关阅读:
    大话设计模式C++ 适配器模式
    36. Valid Sudoku
    c++ 常用的数据结构
    《大话设计模式》c++实现 状态模式
    c++ 各种类型转换
    38. Count and Say
    《大话设计模式》c++实现 抽象工厂模式
    66. Plus One
    49. Group Anagrams
    《大话设计模式》c++实现 建造者模式
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448653.html
Copyright © 2020-2023  润新知