【bzoj3782】上学路线 dp+容斥原理+Lucas定理+中国剩余定理

题目描述

小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
" , f[T]);
	return 0;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/7367353.html