【模拟试题】上学路线

【模拟试题】上学路线

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                                                                                                                                                                                                                                               

Solution

这道题如果没有不能经过的点这一要求,那么就是简单的组合公式C(x+y,y)。又因为 1019663265是一个合数,所以说分解之后用Lucas得出的答案进行中国剩余定理解同余方程就得到了最后的总方案数。但是既然要求了不能过指定点,那么就将所有从指定点得来的方案数进行容斥后输出即可。我们令f[i]为从起始点到第i点不经过其他指定点的方案数,那么就对于当前点,减去所有能够到达自己的其他指定点所得到的次数减去即可。

CODE

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline long long read(){
	char c;long long rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
long long n,m,T;
long long mod;
long long fac[5][1000005];
struct Point {long long x,y,z;}p[205];
inline bool cmp(Point a,Point b)
    {return a.x<b.x||a.x==b.x&&a.y<b.y;}
long long Prime[5]={3,5,6793,10007,1000003},cnt,M[5];
long long f[205];
inline long long Mul(long long a,long long b,long long p){
	long long rec=0;
	while(b){
		if(b&1)rec=(rec+a)%p;
		a<<=1;a%=p;b>>=1;
	}return rec;
}
inline long long Ksm(long long a,long long x,long long p){
	long long rec=1;
	while(x){
		if(x&1)rec=Mul(rec,a,p);
		a=Mul(a,a,p);x>>=1;
	}return rec;
}
inline long long C(long long n,long long m,int x){
	if(m>n)return 0;
	return fac[x][n]*Ksm(fac[x][n-m]*fac[x][m],Prime[x]-2,Prime[x])%Prime[x];
}
inline long long Lucas(long long n,long long m,int x){
	if(m==0)return 1;
	return C(n%Prime[x],m%Prime[x],x)*Lucas(n/Prime[x],m/Prime[x],x)%Prime[x];
}
inline long long Ex_Gcd(long long a,long long b,long long &x,long long &y){
	if(b==0){x=1;y=0;return a;}
	long long gcd=Ex_Gcd(b,a%b,x,y);
	long long t=x;x=y;y=t-a/b*y;
	return gcd;
}
inline long long CRT(){
	long long x,y,a=0,m,k=1,ans;
	for(long long i=0;i<4;i++)k*=Prime[i];
	for(long long i=0;i<4;i++){
		m=k/Prime[i];
		Ex_Gcd(Prime[i],m,x,y);
		a=(a+y*m*M[i])%k;
	}
	if(a>0)ans=a;
	else ans=a+k;
	return ans;
}
inline long long Sov(long long n,long long m){
	memset(M,0,sizeof(M));
	if(cnt){
		for(long long i=0;i<4;i++)
			M[i]=Lucas(n,m,i)%Prime[i];
		return CRT();
	}
	else return Lucas(n,m,4)%mod;
}
int main(){
	n=read();m=read();T=read();cin>>mod;
	for(long long i=1;i<=T;i++)p[i].x=read(),p[i].y=read(),p[i].z=p[i].x+p[i].y;
	p[++T].x=n;p[T].y=m;p[T].z=n+m;
	sort(p+1,p+1+T,cmp);
	if(mod==1000003)cnt=0;else cnt=4;
	for(long long i=0;i<5;i++){
		fac[i][0]=1;
		for(long long j=1;j<=Prime[i];j++)
			fac[i][j]=(fac[i][j-1]*j)%Prime[i];
	}
	for(long long i=1;i<=T;i++){
		f[i]=Sov(p[i].z,p[i].x)%mod;
		for(long long j=1;j<i;j++){
			if(p[j].x<=p[i].x&&p[j].y<=p[i].y){
				f[i]=(f[i]-f[j]*Sov(p[i].z-p[j].z,p[i].x-p[j].x)%mod+mod)%mod;
			}
		}
	}cout<<f[T];
	return 0;
}
用int写完之后无脑替换为long long。。。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值