BZOJ 3782 上学路线 动态规划+Lucas定理

题目大意:给定一张 NM 的网格图,有 T 个坏点,求左上角走到右下角的方案数对 P 取模后的值

首先把坏点和终点以 x 坐标为第一键值, y 坐标为第二键值排序
fi 表示从原点不经过任何坏点走到第 i 个点的个数,那么有DP方程:
fi=Cxixi+yixj<=xi,yj<=yiC(xixj)(xixj)+(yiyj)fj
这个相当于枚举第一个遇到的坏点是啥,然后从总方案里减掉

然后就是组合数取模的WT了= =
P=1000003 ,直接上Lucas定理
P=1019663265 ,那么 P=35679310007 ,对每个质数上Lucas定理,然后用中国剩余定理合并

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 220
using namespace std;
struct Point{
    long long x,y;
    friend istream& operator >> (istream &_,Point &p)
    {
        return scanf("%I64d%I64d",&p.x,&p.y),_;
    }
    bool operator < (const Point &p) const
    {
        if( x != p.x )
            return x < p.x ;
        return y < p.y ;
    }
}points[M];
long long n,m,t,p;
long long f[M];
void Assert(bool flag)
{
    if(!flag)
    {
        puts("Fuck♂You!");
        exit(0);
    }
}
long long Quick_Power(long long x,long long y,long long p)
{
    long long re=1;
    while(y)
    {
        if(y&1) (re*=x)%=p;
        (x*=x)%=p; y>>=1;
    }
    return re;
}
namespace P1{
    long long fac[1000003],inv[1000003];
    void Linear_Shaker()
    {
        int i;
        for(fac[0]=1,i=1;i<p;i++)
            fac[i]=fac[i-1]*i%p;
        for(inv[1]=1,i=2;i<p;i++)
            inv[i]=(p-p/i)*inv[p%i]%p;
        for(inv[0]=1,i=1;i<p;i++)
            (inv[i]*=inv[i-1])%=p;
    }
    long long C(long long n,long long m)
    {
        if(n<m)
            return 0;
        if(n<p&&m<p)
            return fac[n] * inv[m] * inv[n-m] % p;
        return C(n%p,m%p) * C(n/p,m/p) % p ;
    }
}
namespace P2{
    int prime[]={3,5,6793,10007};
    long long fac[4][10007],inv[4][10007];
    void Linear_Shaker()
    {
        int i,j;
        for(j=0;j<4;j++)
        {
            int p=prime[j];
            long long *fac=P2::fac[j];
            long long *inv=P2::inv[j];
            for(fac[0]=1,i=1;i<p;i++)
                fac[i]=fac[i-1]*i%p;
            for(inv[1]=1,i=2;i<p;i++)
                inv[i]=(p-p/i)*inv[p%i]%p;
            for(inv[0]=1,i=1;i<p;i++)
                (inv[i]*=inv[i-1])%=p;
        }
    }
    long long C(long long n,long long m,int j)
    {
        int p=prime[j];
        long long *fac=P2::fac[j];
        long long *inv=P2::inv[j];
        if(n<m)
            return 0;
        if(n<p&&m<p)
            return fac[n] * inv[m] * inv[n-m] % p;
        return C(n%p,m%p,j) * C(n/p,m/p,j) % p ;
    }
    long long C(long long n,long long m)
    {
        long long r1=C(n,m,0);
        long long r2=C(n,m,1);
        long long r3=C(n,m,2);
        long long r4=C(n,m,3);
        return (r1*339887755+r2*407865306+r3*673070820+r4*618502650)%p;
    }
}
void Pretreatment()
{
    if(p==1000003)
        P1::Linear_Shaker();
    else
        P2::Linear_Shaker();
}
long long C(long long n,long long m)
{
    if(p==1000003)
        return P1::C(n,m);
    else
        return P2::C(n,m);
}
int main()
{
    int i,j;
    cin>>n>>m>>t>>p;
    //Assert(p==1000003||p==1019663265);
    for(i=1;i<=t;i++)
        cin>>points[i];
    points[++t].x=n;
    points[  t].y=m;
    sort(points+1,points+t+1);
    Pretreatment();
    for(i=1;i<=t;i++)
    {
        f[i]=C(points[i].x+points[i].y,points[i].x);
        for(j=1;j<i;j++)
            if(points[j].y<=points[i].y)
                (f[i]+=(p-f[j]*C(points[i].x-points[j].x+points[i].y-points[j].y,points[i].x-points[j].x)%p))%=p;
    }
    cout<<f[t]<<endl;
    return 0;
}

你可能感兴趣的:(动态规划,bzoj,Lucas定理,BZOJ3782)