BZOJ 3782 上学路线

216 篇文章 0 订阅
160 篇文章 0 订阅

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;
}




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值