【BZOJ4767】两双手(动态规划,容斥)

【BZOJ4767】两双手(动态规划,容斥)

题面

BZOJ

题解

发现走法只有两种,并且两维坐标都要走到对应的位置去。
显然对于每个确定的点,最多只有一种固定的跳跃次数能够到达这个点。
首先对于每个点都计算出两种跳跃方法的次数。
然后按照跳跃次数排序。
显然只可能从跳跃次数少的跳跃到跳跃次数多的点。
考虑\(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;
}

posted @ 2018-07-02 15:32  小蒟蒻yyb  阅读(423)  评论(0编辑  收藏  举报