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

点此看题面

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

转化

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

则显然可以列出方程组:

\[\begin{cases}Ax\cdot x+Bx\cdot y=a&①\\Ay\cdot x+By\cdot y=b&②\end{cases} \]


如果\(Bx=0\),则:

\[Ax\cdot x=a \]

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

\[x=\frac{a}{Ax} \]


如果\(Bx≠0\),则:

\[①\times\frac{By}{Bx}-②:(\frac{Ax\cdot By}{Bx}-Ay)x=\frac{a\cdot By}{Bx}-b \]

因为\(Ax\cdot By-Ay\cdot Bx≠0\),所以\(\frac{Ax\cdot By}{Bx}-Ay≠0\),因此:

\[x=\frac{\frac{a\cdot By}{Bx}-b}{\frac{Ax\cdot 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\times 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;
}
posted @ 2020-02-03 15:02  TheLostWeak  阅读(...)  评论(...编辑  收藏