[2016-3-14 Test][BZOJ 3782]上学路线

10 篇文章 0 订阅
8 篇文章 0 订阅

上学路线

(path.pas/c/cpp)

source[2015-2-28测试]

 

问题描述:

小C所在的城市的道路构成了一个方形网格,它的西南角为(0,0),东北角为(N,M)。小C家住在西南角,学校在东北角。现在有T个路口进行施工,小C不能通过这些路口。小C喜欢走最短的路径到达目的地,因此他每天上学时都只会向东或北行走;而小C又喜欢走不同的路径,因此他问你按照他走最短路径的规则,他可以选择的不同的上学路线有多少条。由于答案可能很大,所以小C只需要让你求出路径数modP的值。

 

输入:

第一行,四个整数N、M、T、P。

接下来的T行,每行两个整数,表示施工的路口的坐标。

 

输出:

一行,一个整数,路径数mod P的值。

 

输入输出样例:

path.in

path.out

3 4 3 1019663265           

3 0

1 1

2 2

 

8

 

 

数据范围:

测试点编号

N、M的范围

T的范围

P的范围

1

1<=N、M<=1000

0<=T<=200

P=1019663265

2

3

1<=N、M<=100000

T=0

P=1000003

4

5

1<=N、M<=109

P=1019663265

6

7

1<=N、M<=100000

0<=T<=200

P=1000003

8

9

1<=N、M<=109

P=1019663265



容斥原理,中国剩余定理,dp,Lucas.

为此题点赞



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 210
using namespace std;
typedef long long ll;
const int mod[] = {1000003, 3, 5, 6793, 10007};

ll power_mod(ll a, ll b, ll mod){
	ll ret = 1;
	while(b){
		if(b & 1)ret = ret * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return ret % mod;
}


struct Point{
	ll x, y;
	Point(ll x = 0, ll y = 0):x(x), y(y){}
	bool operator<(const Point& k)const{
		return x <= k.x && y <= k.y;
	}
}b[maxn];

bool cmp(const Point& p, const Point& k){
	if(p.x != k.x)return p.x < k.x;
	return p.y < k.y;
}
#define maxM 1000010
ll dp[5][maxn], fac[5][maxM], inv[5][maxM];


ll C(ll n, ll m, int type){
	if(n < m)return 0;
	ll p = mod[type];
	if(!inv[type][m])inv[type][m] = power_mod(fac[type][m], p - 2, p);
	if(!inv[type][n - m])inv[type][n - m] = power_mod(fac[type][n - m], p - 2, p);
	return fac[type][n] * inv[type][m] % p * inv[type][n - m] % p;
}

ll Lucas(ll n, ll m, int type){
	if(m == 0)return 1;
	return Lucas(n / mod[type], m / mod[type], type)
		* C(n % mod[type], m % mod[type], type) % mod[type];
}

ll n, m, T;
ll P;
ll CRT(){
	ll ret = 0, M = P;
	for(int i = 1; i <= 4; i ++){
		ll p = mod[i], w = M / p, inv = power_mod(w, p - 2, p);
		w = w * inv % M;
		ret += w * dp[i][T] % M;
		ret %= M;
	}
	ret %= M;
	if(ret < 0)ret += M;
	return ret;
}

int main(){
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	scanf("%lld%lld%d%lld", &n, &m, &T, &P);
	for(int i = 1; i <= T; i ++)
	    scanf("%d%d", &b[i].x, &b[i].y);
	b[++ T] = Point(n, m);
	sort(b + 1, b + 1 + T, cmp);
	if(P == 1000003){
		fac[0][0] = inv[0][1] = 1;
		for(int i = 1; i <= P; i ++)
		    fac[0][i] = fac[0][i - 1] * i % P;
		
		for(int i = 1; i <= T; i ++){
			dp[0][i] = Lucas(b[i].x + b[i].y, b[i].x, 0);
			for(int j = 1; j < i; j ++){
				if(b[j] < b[i]){
                    dp[0][i] -= dp[0][j] * Lucas(b[i].x + b[i].y - b[j].x - b[j].y, b[i].x - b[j].x, 0) % P;
                    dp[0][i] %= P;
				}
			}
			if(dp[0][i] < 0)dp[0][i] += P;
		}
		printf("%lld\n", dp[0][T]);
		return 0;
	}
	
	for(int i = 1; i <= 4; i ++)
	    fac[i][0] = inv[i][1] = 1;
	for(int i = 1; i <= 4; i ++)
		for(int j = 1; j <= mod[i]; j ++)
		    fac[i][j] = fac[i][j - 1] * j % mod[i];
	for(int dir = 1; dir <= 4; dir ++){
        for(int i = 1; i <= T; i ++){
			dp[dir][i] = Lucas(b[i].x + b[i].y, b[i].x, dir);
			for(int j = 1; j < i; j ++){
				if(b[j] < b[i]){
                    dp[dir][i] -= dp[dir][j] * Lucas(b[i].x + b[i].y - b[j].x - b[j].y, b[i].x - b[j].x, dir) % P;
                    dp[dir][i] %= P;
				}
			}
		}
	}
	printf("%lld\n", CRT());
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值