zoukankan      html  css  js  c++  java
  • 【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;
    }
    

     

  • 相关阅读:
    高斯消元
    丑数
    扩展欧几里得算法与线性同余方程
    数论-求逆元
    数论-快速幂-快速乘
    宋逸轩长难句 2
    宋逸轩长难句 1
    c语言 文件
    c语言程序结构
    c语言结构类型
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7367353.html
Copyright © 2011-2022 走看看