[BZOJ4767]两双手

bzoj

description

一个(n imes 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)步,那么方案数显然就是(inom{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] imes 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
",f[n]);return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/9255001.html