【BZOJ4767】两双手(容斥+DP)

点此看题面

大致题意: 在一个无限的二维平面的((0,0))上有一只马,马有两种跳动方式,分别是将横纵坐标各加上(Ax,Ay)(Bx,By)(Axcdot By-Aycdot Bx≠0))。问马跳到点((Ex,Ey))且不经过任意障碍点的方案数。

转化

我们假设一个点((a,b))可以经过(x)次第一种跳法和(y)次第二种跳法跳到。

则显然可以列出方程组:

[egin{cases}Axcdot x+Bxcdot y=a&①\Aycdot x+Bycdot y=b&②end{cases} ]


如果(Bx=0),则:

[Axcdot x=a ]

因为(Axcdot By-Aycdot Bx≠0),所以此时(Ax)必然不等于(0),因此:

[x=frac{a}{Ax} ]


如果(Bx≠0),则:

[① imesfrac{By}{Bx}-②:(frac{Axcdot By}{Bx}-Ay)x=frac{acdot By}{Bx}-b ]

因为(Axcdot By-Aycdot Bx≠0),所以(frac{Axcdot By}{Bx}-Ay≠0),因此:

[x=frac{frac{acdot By}{Bx}-b}{frac{Axcdot By}{Bx}-Ay} ]


同理,我们也可以求出(y)

而当(x<0)(y<0)(x,y)不为整数时,当前点就无法到达。

如果我们以((x,y))为每个点新的坐标,就把原先的问题转化成了一个很套路的坐标系上走路问题:

((0,0))出发,每次只能向上或向右走一个单位,且不能经过任意障碍点,求到达终点的方案数。

容斥+(DP)

我们除去横纵坐标比终点大的障碍点,然后把所有障碍点按坐标排序,最后把终点加到所有障碍点之后成为第(n)个障碍点。

(f_i)为到达第(i)个障碍点且不经过之前任意障碍点的方案数,则答案就是(f_n)

现在,要考虑如何求(f_i)

于是就想到了容斥

既然要不经过之前任意障碍点,我们就求出经过之前若干障碍点的方案数,然后用总方案数减去非法方案数就是合法方案数。

则,如何求出非法方案数呢?

考虑既然经过了障碍点,且障碍点又是有序的,我们可以枚举之前经过的第一个障碍点(j)

那么,在到达(j)之前,不能经过任何障碍点;到达(j)与到达(i)之间,可以任意走。

如果定义一个函数(tot(p,q))表示从第(p)个障碍点到第(q)个障碍点的方案数,那么就有转移方程:

[f_i=tot(0,i)-sum_{j=1}^{i-1}f_j imes tot(j,i) ]

(tot(p,q))呢,实际上可以用组合数直接计算,就是(C_{x_q-x_p+y_q-y_p}^{x_q-x_p})

于是这道题就做完了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500
#define V 500000
#define X 1000000007
#define eps 1e-8
#define C(x,y) (1LL*Fac[x]*IFac[y]%X*IFac[(x)-(y)]%X)
using namespace std;
int n,ax,ay,bx,by,f[N+5],Fac[V+5],IFac[V+5];
struct Point
{
	int x,y;
	I bool operator < (Con Point& o) Con {return x^o.x?x<o.x:y<o.y;}//按坐标排序
	I bool Trans(CI a,CI b)//坐标转化
	{
		double tx=bx?(1.0*a*by/bx-b)/(1.0*ax*by/bx-ay):1.0*a/ax;
		double ty=ax?(1.0*a*ay/ax-b)/(1.0*bx*ay/ax-by):1.0*a/bx;
		return x=tx+0.5,y=ty+0.5,x>=0&&y>=0&&fabs(x-tx)<eps&&fabs(y-ty)<eps;
	}
}e,s[N+5];
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
	RI i,j,x,y;scanf("%d%d%d%d%d%d%d",&x,&y,&n,&ax,&ay,&bx,&by);
	if(!e.Trans(x,y)) return puts("0"),0;//无法到达终点输出0
	RI lim=e.x+e.y;for(Fac[0]=i=1;i<=lim;++i) Fac[i]=1LL*Fac[i-1]*i%X;
	for(IFac[lim]=Qpow(Fac[lim],X-2),i=lim-1;~i;--i) IFac[i]=1LL*IFac[i+1]*(i+1)%X;
	for(i=1;i<=n;++i) scanf("%d%d",&x,&y),(!s[i].Trans(x,y)||s[i].x>e.x||s[i].y>e.y)&&(--n,--i);//无法到达的点直接删去
	#define tot(A,B) (s[A].x<=s[B].x&&s[A].y<=s[B].y?C(s[B].x-s[A].x+s[B].y-s[A].y,s[B].x-s[A].x):0)
	s[++n]=e,sort(s+1,s+n+1),f[0]=1;
	for(i=1;i<=n;++i) for(f[i]=tot(0,i),j=1;j^i;++j) f[i]=(f[i]-1LL*f[j]*tot(j,i)%X+X)%X;//DP
	return printf("%d",f[n]),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4767.html