[BZOJ3782]上学路线

Posted by Dispwnl on February 13, 2019

题目

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

题解

如果没有不能走的位置,路径条数就是$C_{n+m}^{n}$

设$f_i$表示前$i-1$个障碍不走,走到第$i$个障碍的路径条数,那么有转移:

$f_i=C_{x_i+y_i}^{x_i}-\sum_{j=1}^{i-1}f_j\times C_{(x_i-x_j)+(y_i-y_j)}^{x_i-x_j},x_j\le x_i,y_j\le y_i$

表示全部的路径条数减去不合法的路径条数

发现模数有两个,一个挺小并且是质数,所以组合数直接用$Lucas$解决就行了

另一个不是质数,但是能分解成$3,5,6793,10007$,所以用$Lucas$搞出来$4$个质数的解再用$crt$合并就行了

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long
using namespace std;
const int MAX=1e6+5,N=2e4+5;
struct p{
	LL x,y;
	bool operator< (const p &a)
	const{
		if(x!=a.x) return x<a.x;
		return y<a.y;
	}
}A[N];
LL n,m;
int T,P;
int _P[4]={3,5,6793,10007};
int Pow(int a,int b,int mod)
{
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod)
	  if(b&1) ans=1ll*ans*a%mod;
	return ans;
}
int Csmall(int a,int b,int mod,int *fac)
{
	if(a>b) return 0;
	return 1ll*fac[b]*Pow(fac[a],mod-2,mod)%mod*Pow(fac[b-a],mod-2,mod)%mod;
}
int C(LL a,LL b,int mod,int *fac)
{
	if(a>b) return 0;
	if(!b) return 1;
	return 1ll*C(a/mod,b/mod,mod,fac)%mod*Csmall(a%mod,b%mod,mod,fac)%mod;
}
int exgcd(int a,int b,int &x,int &y)
{
	if(!b) return x=1,y=0,a;
	int ans=exgcd(b,a%b,x,y),tt=x;
	return x=y,y=tt-a/b*y,ans;
}
struct Sub_1{
	int fac[MAX],f[N];
	void Solve()
	{
		fac[0]=fac[1]=1;
		for(int i=2;i<=P;++i)
		  fac[i]=1ll*fac[i-1]*i%P;
		for(int i=1;i<=T;++i)
		  {
		  	f[i]=C(A[i].x,A[i].x+A[i].y,P,fac);
		  	for(int j=1;j<i;++j)
		      if(A[i].x>=A[j].x&&A[i].y>=A[j].y) f[i]=(f[i]-1ll*f[j]*C(A[i].x-A[j].x,A[i].x-A[j].x+A[i].y-A[j].y,P,fac)%P+P)%P;
		  }
		printf("%d",f[T]);
	}
}OAO;
struct Sub_2{
	int f[N];
	int fac[4][N],inv[4][N],B[4];
	int _C(LL a,LL b)
	{
		int ans=0;
		for(int i=0,M,x,y;i<4;++i)
		  M=P/_P[i],exgcd(M,_P[i],x,y),ans=(ans+1ll*M*((x%_P[i]+_P[i])%_P[i])%P*C(a,b,_P[i],fac[i])%P)%P;
		return ans;
	}
	void Solve()
	{
		for(int j=0;j<4;++j)
		  {
		  	fac[j][0]=fac[j][1]=inv[j][1]=1;
		  	for(int i=2;i<=_P[j];++i)
			  fac[j][i]=1ll*fac[j][i-1]*i%_P[j],inv[j][i]=(-_P[j]/i*inv[j][_P[j]%i]%_P[j]+_P[j])%_P[j];
		  }
		for(int i=1;i<=T;++i)
		  {
		  	f[i]=_C(A[i].x,A[i].x+A[i].y);
		  	for(int j=1;j<i;++j)
		      if(A[i].x>=A[j].x&&A[i].y>=A[j].y) f[i]=(f[i]-1ll*f[j]*_C(A[i].x-A[j].x,A[i].x-A[j].x+A[i].y-A[j].y)%P+P)%P;
		  }
		printf("%d",f[T]);
	}
}OUO;
int main()
{
	scanf("%lld%lld%d%d",&n,&m,&T,&P);
	for(int i=1;i<=T;++i)
	  scanf("%d%d",&A[i].x,&A[i].y);
	A[++T]=(p){n,m},sort(A+1,A+1+T);
	if(P==1e6+3) return OAO.Solve(),0;
	return OUO.Solve(),0;
}