BZOJ 3782 上學路線

Description

小C所在的城市的道路構成了一個方形網格,它的西南角爲(0,0),東北角爲(N,M)。小C家住在西南角,學校在東北角。現在有T個路口進行施工,小C不能通過這些路口。小C喜歡走最短的路徑到達目的地,因此他每天上學時都只會向東或北行走;而小C又喜歡走不同的路徑,因此他問你按照他走最短路徑的規則,他可以選擇的不同的上學路線有多少條。由於答案可能很大,所以小C只需要讓你求出路徑數mod P的值。

Input

第一行,四個整數N、M、T、P。
接下來的T行,每行兩個整數,表示施工的路口的座標。

Output

一行,一個整數,路徑數mod P的值。

Sample Input

3 4 3 1019663265
3 0
1 1
2 2

Sample Output

8

HINT

1<=N,M<=10^10

0<=T<=200

p=1000003或p=1019663265

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DP+lucas定理+中國剩餘定理~

用f[i]表示到第i個斷點且沒有經過其餘任何斷點的路徑總數,那麼我們把斷點排序,f[i]=c(f[i].x+f[i].y,f[i].x)-sum{f[j]*C(a[i].x+a[i].y-a[j].x-a[j].y,a[i].x-a[j].x)},其中j<i && a[j].x<=a[i].x && a[j].y<=a[i].y。

所以要求的就是組合數……模數爲1000003時,它是個質數,所以直接用lucas定理就行了,模數爲1019663265時,它不是質數,就要用中國剩餘定理。

其實沒必要開long long的,我只是懶得改了而已,略略略。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> 
using namespace std;
#define ll long long

ll n,m,mod,tot,f[202],pri[]={0,3,5,6793,10007};

struct node{
	ll x,y;
	bool operator < (const node&u) const
	{
		return x==u.x ? y<u.y:x<u.x;
	}
}a[202];

ll read()
{
	ll x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

ll mi(ll u,ll v)
{
	ll now=1;
	for(;v;v>>=1,u=u*u%mod) if(v&1) now=now*u%mod;
	return now; 
}

struct luca1{
	ll sheng[1000005],jiang[1000005];
	void init()
	{
		sheng[0]=jiang[0]=1;
		for(int i=1;i<mod;i++) sheng[i]=sheng[i-1]*i%mod;
		jiang[mod-1]=mi(sheng[mod-1],mod-2);
		for(int i=mod-2;i;i--) jiang[i]=jiang[i+1]*(i+1)%mod;
	}
	ll c(ll n,ll m)
	{
		if(n<m) return 0;
		if(n<mod && m<mod) return sheng[n]*jiang[m]%mod*jiang[n-m]%mod;
		return c(n%mod,m%mod)*c(n/mod,m/mod)%mod;
	}
}lu1;

struct luca2{
	ll sheng[5][1000005],jiang[5][1000005];
	void init()
	{
		for(int k=1;k<=4;k++)
		{
			ll mod=pri[k];
			sheng[k][0]=jiang[k][0]=1;
			for(int i=1;i<mod;i++) sheng[k][i]=sheng[k][i-1]*i%mod;
			jiang[k][mod-1]=mi(sheng[k][mod-1],mod-2);
			for(int i=mod-2;i;i--) jiang[k][i]=jiang[k][i+1]*(i+1)%mod;
		}
	}
	ll c(ll n,ll m,ll mod,int k)
	{
		if(n<m) return 0;
		if(n<mod && m<mod) return sheng[k][n]*jiang[k][m]%mod*jiang[k][n-m]%mod;
		return c(n/mod,m/mod,mod,k)*c(n%mod,m%mod,mod,k)%mod;
	}
	void gcd(ll a,ll b,ll &x,ll &y)
	{
		if(!b)
		{
			x=1;y=0;return;
		}
		gcd(b,a%b,y,x);y-=a/b*x;
	}
	void add(ll &a,ll b)
	{
		a=a+b>=mod ? a+b-mod:a+b; 
	}
	ll crt(ll n,ll m)
	{
		ll tmp,x,y,ans=0;
		for(int k=1;k<=4;k++)
		{
			tmp=mod/pri[k];
			gcd(tmp,pri[k],x,y);
			add(ans,tmp*x*c(n,m,pri[k],k)%mod);
		}
		return ans;
	}
}lu2;

ll C(ll n,ll m)
{
	return mod==1000003 ? lu1.c(n,m):lu2.crt(n,m);
}

int main()
{
	n=read();m=read();tot=read();mod=read();
	for(int i=1;i<=tot;i++) a[i]=(node){read(),read()};
	a[++tot]=(node){n,m};
	sort(a+1,a+tot);
	if(mod==1000003) lu1.init();
	else lu2.init();
	for(int i=1;i<=tot;i++)
	{
		f[i]=C(a[i].x+a[i].y,a[i].x);
		for(int j=1;j<i;j++) if(a[j].y<=a[i].y)
		  f[i]=(f[i]-f[j]*C(a[i].x+a[i].y-a[j].x-a[j].y,a[i].x-a[j].x)%mod)%mod;
		f[i]=(f[i]+mod)%mod;
	}
	printf("%lld\n",f[tot]);
	return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章