题目描述
小C所在的城市的道路构成了一个方形网格,它的西南角为(0,0),东北角为(N,M)。小C家住在西南角,学校在东北角。现在有T个路口进行施工,小C不能通过这些路口。小C喜欢走最短的路径到达目的地,因此他每天上学时都只会向东或北行走;而小C又喜欢走不同的路径,因此他问你按照他走最短路径的规则,他可以选择的不同的上学路线有多少条。由于答案可能很大,所以小C只需要让你求出路径数mod P的值。
输入
第一行,四个整数N、M、T、P。
接下来的T行,每行两个整数,表示施工的路口的坐标。
输出
一行,一个整数,路径数mod P的值。
样例输入
3 4 3 1019663265
3 0
1 1
2 2
样例输出
8
提示
p=1000003 或 p=1019663265
题解
dp+Lucas定理+中国剩余定理
设$f[i]$表示从$(0,0)$走到第i个坏点(终点算作第T+1个坏点),中途不经过其它坏点的方案数。
那么直接求$f[i]$比较困难,考虑单步容斥,用总方案数-经过坏点的方案数推出$f[i]$。
从$(0,0)$走到$(n,m)$的总方案数为$C_{n+m}^n$,可以看做总共n+m步,其中n步是x方向。
而经过坏点的方案数,枚举其经过的第一个坏点,那么它的贡献为|从$(0,0)走到该点,中途不经过其它坏点的方案数|*|从这个坏点走到当前点的方案数|。
第一个即为坏点的$f$值,第二个用组合数求法求出。
最后的答案就是$f[T+1]$。
然而本题较为恶心之处在于模数,当p=1000003时可以直接使用Lucas定理,而当p=1019663265时p不为质数,将其分解质因数为3*5*6793*10007,使用Lucas定理分别求出组合数在模这些质因子意义下的值,再使用中国剩余定理CRT合并,才能得到组合数模1019663265的值。
细节还是有点多,代码已经差不多优化到极限了,凑合着看吧。。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const ll mod[] = {1000003 , 3 , 5 , 6793 , 10007} , temp = 1019663265; struct data { ll x , y; bool operator<(const data a)const {return x == a.x ? y < a.y : x < a.x;} }a[210]; ll f[210] , fac[5][1000010] , d[5]; bool flag; ll pow(ll x , ll y , ll p) { ll ans = 1; while(y) { if(y & 1) ans = ans * x % p; x = x * x % p , y >>= 1; } return ans; } ll choose(ll n , ll m , ll p) { if(n < m) return 0; if(n < mod[p] && m < mod[p]) return fac[p][n] * pow(fac[p][m] , mod[p] - 2 , mod[p]) % mod[p] * pow(fac[p][n - m] , mod[p] - 2 , mod[p]) % mod[p]; return choose(n / mod[p] , m / mod[p] , p) * choose(n % mod[p] , m % mod[p] , p) % mod[p]; } ll C(ll n , ll m) { if(!flag) return choose(n , m , 0); int i; ll ans = 0; for(i = 1 ; i < 5 ; i ++ ) d[i] = choose(n , m , i); for(i = 1 ; i < 5 ; i ++ ) ans = (ans + temp / mod[i] * pow(temp / mod[i] , mod[i] - 2 , mod[i]) % temp * d[i] % temp) % temp; return ans; } int main() { ll n , m; int p , T , i , j; scanf("%lld%lld%d%d" , &n , &m , &T , &p) , flag = (p != 1000003); for(i = 0 ; i < 5 ; i ++ ) for(fac[i][0] = j = 1 ; j < mod[i] ; j ++ ) fac[i][j] = fac[i][j - 1] * j % mod[i]; for(i = 1 ; i <= T ; i ++ ) scanf("%lld%lld" , &a[i].x , &a[i].y); a[++T].x = n , a[T].y = m; sort(a + 1 , a + T + 1); for(i = 1 ; i <= T ; i ++ ) { f[i] = C(a[i].x + a[i].y , a[i].x); for(j = 1 ; j < i ; j ++ ) if(a[j].y <= a[i].y) f[i] = (f[i] - f[j] * C(a[i].y - a[j].y + a[i].x - a[j].x , a[i].y - a[j].y) % p + p) % p; } printf("%lld\n" , f[T]); return 0; }