[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 \le N,M \le 10^{10}\)
\(0 \le T \le 200\)
\(p=1000003\)或\(p=1019663265\)
sol
容斥,方法同bzoj4767 两双手,就是对于每个位置,枚举不合法的情况经过的第一个不合法点,复杂度\(O(T^2)\)。
发现这里的组合数比较大,所以就卢卡斯一波。
\(10^6+3\)是一个质数,可以直接卢卡斯。
\(1019663265=3\times5\times6793\times10007\),所以还得套个\(CRT\)合并。
code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 1e6+5;
struct node{
ll x,y;
bool operator < (const node &b) const{
return x==b.x?y<b.y:x<b.x;
}
}a[205];
int p[5]={1000003,3,5,6793,10007};
int k,mod,inv[5][N],jc[5][N],jcn[5][N],f[205];
ll n,m;
int C(ll n,ll m,int op){
if (n<m) return 0;
if (n<p[op]&&m<p[op]) return 1ll*jc[op][n]*jcn[op][m]%p[op]*jcn[op][n-m]%p[op];
return 1ll*C(n/p[op],m/p[op],op)*C(n%p[op],m%p[op],op)%p[op];
}
int cal(ll x,ll y){
if (x<0||y<0) return 0;
if (mod==p[0]) return C(x+y,y,0);
int res=0;
for (int i=1;i<=4;++i){
int tmp=C(x+y,y,i),Inv=inv[i][mod/p[i]%p[i]];
res=(res+1ll*Inv*(mod/p[i])%mod*tmp%mod)%mod;
}
return res;
}
int main(){
for (int i=0;i<=4;++i){
inv[i][0]=inv[i][1]=jc[i][0]=jcn[i][0]=1;
for (int j=2;j<p[i];++j) inv[i][j]=1ll*inv[i][p[i]%j]*(p[i]-p[i]/j)%p[i];
for (int j=1;j<p[i];++j) jc[i][j]=1ll*jc[i][j-1]*j%p[i],jcn[i][j]=1ll*jcn[i][j-1]*inv[i][j]%p[i];
}
scanf("%lld%lld%d%d",&n,&m,&k,&mod);
for (int i=1;i<=k;++i) scanf("%lld%lld",&a[i].x,&a[i].y);
a[++k]=(node){n,m};sort(a+1,a+k+1);
for (int i=1;i<=k;++i){
f[i]=cal(a[i].x,a[i].y);
for (int j=1;j<i;++j) f[i]=(f[i]-1ll*f[j]*cal(a[i].x-a[j].x,a[i].y-a[j].y)%mod+mod)%mod;
}
printf("%d\n",f[k]);return 0;
}