【BZOJ4767】两双手(动态规划,容斥)
【BZOJ4767】两双手(动态规划,容斥)
题面
题解
发现走法只有两种,并且两维坐标都要走到对应的位置去。
显然对于每个确定的点,最多只有一种固定的跳跃次数能够到达这个点。
首先对于每个点都计算出两种跳跃方法的次数。
然后按照跳跃次数排序。
显然只可能从跳跃次数少的跳跃到跳跃次数多的点。
考虑\(f[i]\)表示到达第\(i\)个点且不经过前面任何一个障碍点的方案数。
\(f[i]=C_{x+y}^x\),其中\(x,y\)表示两种方法跳跃的次数。
然后容斥减去经过前面任何一个点的方案数就好了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1111111
#define MOD 1000000007
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int Ex,Ey,Ax,Ay,Bx,By,n;
struct Node{int x,y;}p[MAX];
bool cmp(Node a,Node b){if(a.x==b.x)return a.y<b.y;return a.x<b.x;}
int jc[MAX],jv[MAX],inv[MAX],f[MAX];
int C(int n,int m){if(n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
void Calc(int &x,int &y)
{
ll a1,a2,b1,b2;
a1=x*By-y*Bx;a2=Ax*By-Ay*Bx;
b1=x*Ay-Ax*y;b2=Bx*Ay-Ax*By;
if(a2==0||b2==0){x=-1;y=-1;return;}
if((a1/a2)*a2!=a1||(b1/b2)*b2!=b1){x=-1;y=-1;return;}
x=a1/a2;y=b1/b2;
}
int main()
{
Ex=read();Ey=read();n=read();
Ax=read(),Ay=read();Bx=read();By=read();
Calc(Ex,Ey);
for(int i=1;i<=n;++i)
{
p[i].x=read(),p[i].y=read();
Calc(p[i].x,p[i].y);
if(p[i].x<0||p[i].y<0||p[i].x>Ex||p[i].y>Ey)--n,--i;
}
p[++n]=(Node){Ex,Ey};sort(&p[1],&p[n+1],cmp);
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=1;i<MAX;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=2;i<MAX;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<MAX;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
for(int i=1;i<=n;++i)
{
f[i]=C(p[i].x+p[i].y,p[i].x);
if(!f[i])continue;
for(int j=1;j<i;++j)
f[i]=(f[i]-1ll*f[j]*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)%MOD+MOD)%MOD;
}
printf("%d\n",f[n]);
return 0;
}