题解 P4478 【[BJWC2018]上学路线】
考虑 dp。
我们先把
然后设
至于为什么要先排序,是因为我们要保证当处理
显然有状态转移方程:(其中
首先,
证明:从
然后
然后证明那些不合法的方案就是
设某一条不合法路径上的第一个障碍点是第
那么
这么解释的话,就容易证明
那么这个状态转移方程就是正确的了。
现在的问题就是
显然,当
但是当
这个需要用中国剩余定理来做,不会的请先去做一下 【模板】中国剩余定理(CRT)/曹冲养猪。
中国剩余定理的结论大概是这个东西:
对于一个方程组:
其中
设
然后又因为
这个题中,未知数
然后我们还知道
那么我们通过中国剩余定理就可以把
具体代码如下:
#include<bits/stdc++.h>
#define T 210
#define ll long long
using namespace std;
struct Point
{
int x,y;
Point(){};
Point(int a,int b){x=a,y=b;}
}q[T];
int n,m,num,P;
ll f[T];
ll poww(ll a,ll b,ll p)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
namespace mod1//p=1019663265的情况
{
const ll M=1019663265ll,p[5]={0,3,5,6793,10007};
ll m[5],t[5],fac[5][10010],inv[5][10010];
ll a[5];
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-a/b*y;
}
void init()
{
for(int i=1;i<=4;i++)
{
m[i]=M/p[i];
ll x,y;
exgcd(m[i],p[i],x,y);//通过exgcd求逆元
t[i]=(x%p[i]+p[i])%p[i];
}
for(int i=1;i<=4;i++)
{
fac[i][0]=1;
for(int j=1;j<p[i];j++)
fac[i][j]=fac[i][j-1]*j%p[i];
}
for(int i=1;i<=4;i++)
for(int j=0;j<p[i];j++)
inv[i][j]=poww(fac[i][j],p[i]-2,p[i]);
}
ll C(ll n,ll m,int k)
{
if(n<m) return 0;
if(!m) return 1;
return fac[k][n]*inv[k][n-m]%p[k]*inv[k][m]%p[k];
}
ll Lucas(ll n,ll m,int k)
{
if(n<m) return 0;
if(!m) return 1;
return C(n%p[k],m%p[k],k)*Lucas(n/p[k],m/p[k],k)%p[k];
}
ll solve(ll n,ll mm)
{
ll x=0;
for(int i=1;i<=4;i++)
{
a[i]=Lucas(n,mm,i);
x=(x+a[i]*m[i]%M*t[i]%M)%M;//求x的解
}
return x;
}
}
namespace mod2//p=1000003的情况
{
const ll p=1000003;
ll fac[1000005],inv[1000005];
void init()
{
fac[0]=1;
for(int i=1;i<p;i++)
fac[i]=fac[i-1]*i%p;
for(int i=0;i<p;i++)
inv[i]=poww(fac[i],p-2,p);
}
ll C(ll n,ll m)
{
if(n<m) return 0;
if(!m) return 1;
return fac[n]*inv[n-m]%p*inv[m]%p;
}
ll Lucas(ll n,ll m)
{
if(n<m) return 0;
if(!m) return 1;
return C(n%p,m%p)*Lucas(n/p,m/p)%p;
}
}
bool cmp(Point a,Point b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&num,&P);
if(P==1000003) mod2::init();
else mod1::init();
for(int i=1;i<=num;i++)
scanf("%d%d",&q[i].x,&q[i].y);
q[num+1]=Point(n,m);
sort(q+1,q+num+2,cmp);
for(int i=1;i<=num+1;i++)
{
if(P==1000003) f[i]=mod2::Lucas(q[i].x+q[i].y,q[i].x);
else f[i]=mod1::solve(q[i].x+q[i].y,q[i].x);
for(int j=1;j<i;j++)
{
if(q[j].x<=q[i].x&&q[j].y<=q[i].y)
{
if(P==1000003) f[i]=(f[i]-f[j]*(mod2::Lucas(q[i].x+q[i].y-q[j].x-q[j].y,q[i].x-q[j].x))%P+P)%P;
else f[i]=(f[i]-f[j]*(mod1::solve(q[i].x+q[i].y-q[j].x-q[j].y,q[i].x-q[j].x))%P+P)%P;
}
}
}
printf("%lld\n",f[num+1]);
return 0;
}