[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\n",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\n",ans%P);
return 0;
}
你——悟到了么?