[BZOJ3782] 上学路线 [BZOJ3782] 上学路线

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

试题分析

显然求补集更加方便,于是求总方案数-不合法方案数。
不合法的方案定然会经过某一个坏点,所以求出(g_i)表示从起点到第(i)个坏点途中不经过任何坏点的方案数。
那么就是总数-经过其中坏点的方案数。
为了方便,把最后一个点也视作坏点dp就可以了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
//#include<cmath>
#include<algorithm>
 
using namespace std;
#define LL long long
inline LL read(){
    LL x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 1000100;
 
LL f[MAXN+1];
LL N,M,T,P; LL Mod;
 
struct poi{LL x,y;}p[MAXN+1];
bool cmp(poi a,poi b){if(a.x!=b.x) return a.x<b.x; return a.y<b.y;}
LL fac[MAXN+1],ifac[MAXN+1],inv[MAXN+1];
 
inline LL C(LL n,LL m){
    if(n<m) return 0; if(!m) return 1;
    if(n<Mod&&m<Mod) return fac[n]*ifac[m]%Mod*ifac[n-m];
    return C(n%Mod,m%Mod)*C(n/Mod,m/Mod)%Mod;
}
inline void init(){
    ifac[0]=fac[0]=1; inv[1]=1; memset(f,0,sizeof(f));
    for(LL i=2;i<Mod;i++) inv[i]=(Mod-(Mod/i)*inv[Mod%i]%Mod)%Mod;
    for(LL i=1;i<Mod;i++) ifac[i]=ifac[i-1]*inv[i]%Mod;
    for(LL i=1;i<Mod;i++) fac[i]=fac[i-1]*i%Mod; return ;
}
inline LL calc(){
    init();
    for(LL i=1;i<=T;i++){
        f[i]=0;
        for(LL j=1;j<i;j++){
            if(p[j].x<=p[i].x&&p[j].y<=p[i].y){
                f[i]=(f[i]+f[j]*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)%Mod)%Mod; 
            } 
        }
        f[i]=(C(p[i].x+p[i].y,p[i].x)-f[i]+Mod)%Mod; //cout<<i<<":"<<p[i].x<<" "<<p[i].y<<"  :"<<f[i]<<endl;
    } return f[T];
}
inline void exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){x=1; y=0; return ;} exgcd(b,a%b,x,y);
    LL tmp=x; x=y; y=tmp-a/b*x; return ;
}
inline LL Get(LL a,LL c,LL b){
    LL x,y; exgcd(a,b,x,y); x=(x+b)%b;
    return x*a%P*c%P;
}
 
int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    N=read(),M=read(),T=read(),P=read();
    for(LL i=1;i<=T;i++){
        p[i].x=read(),p[i].y=read();
    } sort(p+1,p+T+1,cmp); p[++T].x=N; p[T].y=M;
    if(P==1000003){
        Mod=P; printf("%lld
",calc()); return 0;
    } LL a1,a2,a3,a4,ans=0;
    Mod=3; a1=calc(); Mod=5; a2=calc();
    Mod=6793; a3=calc(); Mod=10007; a4=calc();
    ans=(((ans+Get(P/3,a1,3))%P+Get(P/5,a2,5))%P+Get(P/6793,a3,6793))%P+Get(P/10007,a4,10007);
    ans%=P; printf("%lld
",ans%P);
    return 0;
}