其实呢,扩展中国剩余定理还有一种理解方式:就是你有一坨东西,形如:
$A[i]\equiv B[i](mod$ $P[i])$.
对于这个东西,你可以这么思考:
如果最后能求出一个解,那么这个解的增量一定是 $lcm(P[1],P[2].....).$
所以,只要你能找到一坨 $P[i]$,使得它们的 $lcm$ 等于你想要的东西,你就可以用 $excrt$来解.

p话扯完了,我们步入正题:
假设没有障碍,有 $n$ 行 $m$ 列,那么答案即为 $C_{n+m}^{n}.$
这个东西就代表你一共会走 $n+m$ 步,其中 $n$ 步的方向是向上的.
而如果有障碍,我们考虑按照每一个障碍点一次枚举.
首先,将障碍按照横纵坐标从小到大拍一个序,那么考虑我们枚举到第 $i$ 个障碍.
令 $f[i]$ 表示从起点到第 $i$ 个障碍所走过的合法的方案数,$(x,y)$ 表示横纵坐标.
总方案为 $C_{x+y}^{y},$ 不合法的方案为 $\sum f[前面障碍]$.
如果随便减的话,我们可能减多,所以我们考虑按照策略去减.
我们想让每一次减掉的都互不相同,所以我们考虑枚举第一个不合法的障碍.
即 $C_{x+y}^{y}-\sum_{j=1}^{i-1}f[j]\times calc(j到i的方案数).$
这样,我们等于说是强制性的每次只减掉第一个障碍碰到 $j$ 的方案数,不会减多.
这个题的模数不是素数,所以需要将模数分解成若干个素数,然后依次取模,最后在用 $excrt$ 合并.

Code:

#include <cstdio> 
#include <algorithm>  
#define N 1000006    
#define ll long long   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;    
ll F[N];       
int array[10]={0,3,5,6793,10007};
struct Node {
	ll x,y;  
	Node(ll x=0,ll y=0):x(x),y(y){} 
}arr[N];     
bool cmp(Node a,Node b) {
	return a.x==b.x?a.y<b.y:a.x<b.x; 
}        
ll qpow(ll base,ll k,ll mod) {
	ll tmp=1; 
	for(;k;k>>=1,base=base*base%mod) 
		if(k&1) tmp=tmp*base%mod; 
	return tmp;       
}
struct Lucas { 
	int mod; 
	int fac[N];     
	int inv(int i) { 
		return (int)qpow(i,mod-2,mod); 
	}
	void init(int p) {  
		mod=p,fac[0]=1;   
		for(int i=1;i<=mod;++i) fac[i]=(ll)fac[i-1]*i%mod;    
	} 
    int C(int x,int y) { 
    	if(y>x) return 0; 
    	if(y==0) return 1;    
    	return (int)(1ll*fac[x]*inv(fac[y])%mod*inv(fac[x-y])%mod);      
    }     
    int solve(ll x,ll y) {
    	if(y>x) return 0; 
    	if(y==0) return 1;     
    	return (int)(1ll*solve(x/mod,y/mod)*C(x%mod,y%mod)%mod);   
    }
}comb[8];  
struct excrt { 
	ll arr[N],brr[N];   
	ll exgcd(ll a,ll b,ll &x,ll &y) {
		if(!b) { 
			x=1,y=0; 
			return a; 
		} 
		ll gcd=exgcd(b,a%b,x,y),tmp=x;       
		x=y,y=tmp-a/b*y;      
		return gcd;      
	}    
	ll Excrt() { 
		int i,j; 
		ll ans=arr[1],M=brr[1];        
        for(i=2;i<=4;++i) {
        	ll a=M,b=brr[i],c=arr[i]-ans,gcd,x,y;
        	gcd=exgcd(a,b,x,y),b=abs(b/gcd);            
        	x=(x*(c/gcd)%b+b)%b;           
        	ans+=M*x;   
        	M*=brr[i]/__gcd(brr[i],M);    
        	ans=(ans%M+M)%M; 
        } 
        return ans;    
	}
}crt;            
ll C(ll a,ll b,int ty) {   
	if(ty==0) 
		return comb[0].solve(a,b);   
	else {
		int i,j; 
		for(i=1;i<=4;++i) {                         
			crt.arr[i]=comb[i].solve(a,b); 
			crt.brr[i]=array[i];                     
		}
	} 
	return crt.Excrt();  
}
int main() {
	int i,j,k,flag;   
	// setIO("input"); 
	ll n,m,mod;   
	scanf("%lld%lld%d%lld",&n,&m,&k,&mod),flag=(mod==1019663265);   
	if(!flag) {
		comb[0].init(mod);   
	}
	else { 
		for(i=1;i<=4;++i) 
			comb[i].init(array[i]); 
	}              
	for(i=1;i<=k;++i) 
		scanf("%lld%lld",&arr[i].x,&arr[i].y);           
	arr[++k].x=n,arr[k].y=m;    
	sort(arr+1,arr+1+k,cmp);                                                          
	for(i=1;i<=k;++i) { 
		F[i]=C(arr[i].x+arr[i].y,arr[i].y,flag); 
		for(j=1;j<i;++j) {
			if(arr[j].y<=arr[i].y) 
				F[i]=(F[i]-(F[j]*C(arr[i].x-arr[j].x+arr[i].y-arr[j].y,arr[i].y-arr[j].y,flag)%mod)+mod)%mod;     
		}
	} 
	printf("%lld\n",F[k]);   
	return 0;      
}