• 【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;
    }
    
  • 相关阅读:
    基础算法:求目标值 &字符串反转
    算法计算出股票最佳交易时间点
    Python 设计模式—命令模式
    Python 设计模式—代理模式
    有趣的算法题~单调栈
    令人头大的字符串—算法处理
    WebUI 自动化测试的经典设计模式:PO
    双指针—滑动窗口算法解析
    Python 设计模式—观察者模式
    多线程之读写锁原理
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4767.html
Copyright © 2020-2023  润新知