BZOJ 3782: 上学路线 [Lucas定理 DP]

3782: 上学路线

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 192  Solved: 75
[Submit][Status][Discuss]

Description

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

Input

第一行,四个整数N、M、T、P。
接下来的T行,每行两个整数,表示施工的路口的坐标。

Output

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

HINT

1<=N,M<=10^10
0<=T<=200
p=1000003或p=1019663265

终于A掉了
这道题太可怕了
跟PoPoQQQ大爷的代码拍了好几组数据才改完Bug
 
首先想怎么算方案数
总-不合法
用容斥原理?好麻烦
发现一个点只会对$x,y$都比它大的点产生影响
$f[i]$表示走到点$i$不碰到其他点的方案数
$f[i]={x_i+y_i\choose x_i} - \sum\limits_{x_j<=x_i\ ,\ y_j<=y_i\ ,\ j\neq i}{x_i-x_j+y_i-y_j\choose x_i-x_j}*f[j] $
 
剩下的就是怎么计算组合数了
1000003直接用Lucas定理
1019663265和古代猪文一样是4个质数的乘积,Lucas定理+CRT合并
 
说一下问题:
1.DP时要时刻记住取模、
2.小心n和m溢出int
3.组合数n<m特判在用Lucas定理的时候不能丢!因为模意义下可能发生n<m!!!
 
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e6+5;
inline ll read(){
    char c=getchar();ll x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
ll n,m,P;
int t;
struct Point{
    ll x,y;
    bool operator <(const Point &a)const{
        return x<a.x||(x==a.x&&y<a.y);
    }
}a[205];
namespace A{
    ll P=1000003;
    ll inv[N],fac[N],facInv[N];
    void ini(){
        inv[1]=fac[0]=facInv[0]=1;
        for(int i=1;i<=P;i++){
            if(i!=1) inv[i]=-P/i*inv[P%i]%P;
               inv[i]+=inv[i]<0?P:0;
            fac[i]=fac[i-1]*i%P;
            facInv[i]=facInv[i-1]*inv[i]%P;
        }
    }
    ll C(int n,int m){
        if(n<m) return 0;
        return fac[n]*facInv[m]%P*facInv[n-m];
    }
    ll Lucas(ll n,ll m){
        if(n<m) return 0;
        ll re=1;
        for(;m;n/=P,m/=P) re=re*C(n%P,m%P)%P;
        return re;
    }
    ll f[205];
    void dp(){
        for(int i=1;i<=t;i++){
            f[i]=Lucas(a[i].x+a[i].y,a[i].x);//printf("fo %d %lld\n",i,f[i]);
            for(int j=1;j<i;j++) 
                if(a[j].x<=a[i].x&&a[j].y<=a[i].y)
                    f[i]=(f[i]- Lucas(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)*f[j]%P +P)%P;
                    //printf("oh %d %lld  %lld \n",j ,Lucas(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)*f[j]%P ,f[i]);
            //printf("f %d %lld\n",i,f[i]);
        }
    }
    void solve(){
        ini();
        dp();
        printf("%lld",f[t]);
    }
}
namespace B{
    ll P[5]={1019663265,3,5,6793,10007};
    const int N=1e5+5;
    ll Pow(ll a,ll b,ll P){
        if(a==0) return 0;
        ll re=1;
        for(;b;b>>=1,a=a*a%P)
            if(b&1) re=re*a%P;
        return re;
    }
    ll Inv(ll a,ll P){return Pow(a,P-2,P);}
    ll inv[N][5],fac[N][5],facInv[N][5];
    void ini(){
        for(int j=1;j<=4;j++){
            int MOD=P[j];
            inv[1][j]=fac[0][j]=facInv[0][j]=1;
            for(int i=1;i<=MOD;i++){
                if(i!=1) inv[i][j]=-MOD/i*inv[MOD%i][j]%MOD;
                if(inv[i][j]<0) inv[i][j]+=MOD;
                fac[i][j]=fac[i-1][j]*i%MOD;
                facInv[i][j]=facInv[i-1][j]*inv[i][j]%MOD;
            }
        }
    }
    ll C(int n,int m,int j){//printf("C %d %d %d  %lld %lld\n",n,m,j,fac[n][j],facInv[m][j]);
        if(n<m) return 0;
        ll p=P[j];
        return fac[n][j]*facInv[m][j]%p*facInv[n-m][j]%p;
    }
    ll lucas(ll n,ll m,int j){
        if(n<m) return 0;
        ll MOD=P[0],a=1,p=P[j];
        for(;m;m/=p,n/=p) a=a*C(n%p,m%p,j)%p;
        return a*(MOD/p)%MOD*Inv(MOD/p,p)%MOD;
    }
    ll Lucas(ll n,ll m){//printf("Lucas1 %lld %lld\n",n,m);
        ll re=0,MOD=P[0];
        for(int i=1;i<=4;i++)
            re=(re+lucas(n,m,i))%MOD;
        return re;
    }
     
    ll f[205];
    void dp(){
        for(int i=1;i<=t;i++){
            f[i]=Lucas(a[i].x+a[i].y,a[i].x);//printf("fo %d %lld\n",i,f[i]);
            for(int j=1;j<i;j++) 
                if(a[j].x<=a[i].x&&a[j].y<=a[i].y)
                    f[i]=(f[i]- Lucas(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)*f[j]%P[0] +P[0])%P[0];
                    //printf("oh %d %lld  %lld \n",j ,Lucas(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)*f[j] ,f[i]);
        //    printf("fn %d %lld\n",i,f[i]);
        }
    }
    void solve(){
        ini();
        dp();
        //for(int i=1;i<=t;i++)
        //    printf("Point %lld %lld  %lld\n",a[i].x,a[i].y,f[i]);
        printf("%lld",f[t]);
    }
}
int main(){
    freopen("in","r",stdin);
    n=read();m=read();
    t=read();P=read();
    for(int i=1;i<=t;i++) a[i].x=read(),a[i].y=read();
    a[++t].x=n;a[t].y=m;
    sort(a+1,a+1+t);
    if(P==1000003) A::solve();
    else B::solve();
}

 

 
 
 
posted @ 2017-02-16 20:26  Candy?  阅读(485)  评论(0编辑  收藏  举报