[BZOJ3782]上学路线

bzoj

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;
}

posted @ 2018-07-14 22:04  租酥雨  阅读(410)  评论(0编辑  收藏  举报