[BZOJ4767]两双手

bzoj

description

一个\(n\times m\)的棋盘上,一匹马有且仅有两种走法,分别对应向量\((A_x,A_y)\)\((B_x,B_y)\),保证两向量不共线。有\(n\)个障碍点\((x_i,y_i)\),马不能跳到障碍点上,求从\((0,0)\)走到\((n,m)\)的方案数模\(10^9+7\)

sol

先假设没有障碍。
由于两向量不共线,所以马要从\((0,0)\)跳到\((n,m)\),两种走法分别需要的步数是确定的。这个可以列出方程求解,注意如果解不是整数则说明无解。
如果第一种走法需要\(x\)步,第二种走法需要\(y\)步,那么方案数显然就是\(\binom{x+y}{x}\)
有了障碍点,那么一定就需要容斥。指数级的容斥显然是不行的,需要寻求更高效率的方法。
我们设\(f[i]\)表示从\((0,0)\)出发不经过任何障碍点到达第\(i\)个障碍点的方案数,设\(g[i][j]\)表示从第\(i\)个障碍点随意走到第\(j\)个障碍点(允许经过中间的障碍点)的方案数。这里要先把所有障碍点排序。
这样就存在转移:\(f[i]=g[0][i]-\sum_{j=1}^{i-1}f[j]\times g[j][i]\)(假设第\(0\)个障碍点是\((0,0)\)),也就是枚举第一个经过的障碍点是哪个,然后就可以轻松地做到\(O(n^2)\)的转移。
令第\(n+1\)一个障碍点是\((n,m)\),那么答案就是\(f[n+1]\)了。
注意因为\(A_x,A_y,B_x,B_y\)可以有负数,所以所需的步数可能是\(O(n^2)\)级别的,需要注意预处理组合数的上界。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1000005;
const int mod = 1e9+7;
int Ex,Ey,n,Ax,Ay,Bx,By,inv[N],jc[N],jcn[N],f[N];
struct node{
	int x,y;
	bool operator < (const node &b) const{
		return x==b.x?y<b.y:x<b.x;
	}
}a[N];
void work(int &x,int &y){
	int a1=x*By-y*Bx,a2=Ax*By-Ay*Bx;
	int b1=x*Ay-y*Ax,b2=Ay*Bx-Ax*By;
	if (!a2||!b2) {x=y=-1;return;}
	if (a1/a2*a2!=a1||b1/b2*b2!=b1) {x=y=-1;return;}
	x=a1/a2;y=b1/b2;
}
int C(int n,int m){
	return 1ll*jc[n]*jcn[m]%mod*jcn[n-m]%mod;
}
int cal(int x,int y){
	if (x<0||y<0) return 0;
	return C(x+y,y);
}
int main(){
	Ex=gi();Ey=gi();n=gi();
	Ax=gi();Ay=gi();Bx=gi();By=gi();
	work(Ex,Ey);if (Ex<0&&Ey<0) return puts("0"),0;
	for (int i=1;i<=n;++i){
		a[i].x=gi(),a[i].y=gi();work(a[i].x,a[i].y);
		if (a[i].x<0||a[i].y<0||a[i].x>Ex||a[i].y>Ey) --n,--i;
	}
	a[++n]=(node){Ex,Ey};sort(a+1,a+n+1);
	jc[0]=jcn[0]=inv[0]=inv[1]=1;
	for (int i=2;i<N;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
	for (int i=1;i<N;++i) jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=1ll*jcn[i-1]*inv[i]%mod;
	for (int i=1;i<=n;++i){
		f[i]=cal(a[i].x,a[i].y);
		for (int j=1;j<i;++j)
			f[i]=(f[i]-1ll*f[j]*cal(a[i].x-a[j].x,a[i].y-a[j].y)%mod+mod)%mod;
	}
	printf("%d\n",f[n]);return 0;
}
posted @ 2018-07-02 17:49  租酥雨  阅读(490)  评论(0编辑  收藏  举报