[BZOJ 4767]两双手(组合数学+Dp)
Posted Zars19
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ 4767]两双手(组合数学+Dp)相关的知识,希望对你有一定的参考价值。
Description
老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式。老W下棋时觉得无聊,便
决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让
马从(u,v)移动到(u+Bx,v+By)。小W看见老W的下棋方式,觉得非常有趣,他开始思考一个问题:假设棋盘是个无限
大的二维平面,一开始马在原点(0,0)上,若用老W的两种方式进行移动,他有多少种不同的移动方法到达点(Ex,Ey
)呢?两种移动方法不同当且仅当移动步数不同或某一步所到达的点不同。老W听了这个问题,觉得还不够有趣,他
在平面上又设立了n个禁止点,表示马不能走到这些点上,现在他们想知道,这种情况下马有多少种不同的移动方
法呢?答案数可能很大,你只要告诉他们答案模(10^9+7)的值就行。
Solution
Ax*By-Ay*Bx≠0所以向量A、B的步数是固定的,走法也就有C(m+n,m)种
先对障碍点排序
Dp,f[i]表示到达障碍点i且不经过其他障碍点的方案数(用容斥减去经过其他障碍点的方案数)
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define Mod 1000000007 #define MAXN 500005 typedef long long LL; using namespace std; int n,ax,ay,bx,by; LL fac[MAXN],inv[MAXN],f[MAXN]; struct Node { int x,y; }p[MAXN]; void init() { fac[0]=1,inv[1]=1; for(int i=1;i<MAXN;i++) fac[i]=(fac[i-1]*i)%Mod; for(int i=2;i<MAXN;i++) inv[i]=((Mod-Mod/i)*inv[Mod%i])%Mod; inv[0]=1; for(int i=1;i<=MAXN;i++) inv[i]=(inv[i]*inv[i-1])%Mod; } LL C(LL x,LL y) { if(x<y)return 0; return ((fac[x]*inv[y])%Mod*inv[x-y])%Mod; } bool cmp(Node A,Node B) { if(A.x==B.x)return A.y<B.y; return A.x<B.x; } void calc(int &x,int &y) { int m,n; if((ay*x-ax*y)%(bx*ay-ax*by)){x=-1,y=-1;return;} n=(ay*x-ax*y)/(bx*ay-ax*by); if((by*x-bx*y)%(ax*by-bx*ay)){x=-1,y=-1;return;} m=(by*x-bx*y)/(ax*by-bx*ay); x=m,y=n; } int main() { init(); scanf("%d%d%d",&p[0].x,&p[0].y,&n); scanf("%d%d%d%d",&ax,&ay,&bx,&by); calc(p[0].x,p[0].y); for(int i=1;i<=n;i++) { scanf("%d%d",&p[i].x,&p[i].y); calc(p[i].x,p[i].y); } sort(p+1,p+1+n,cmp); p[n+1]=p[0]; for(int i=1;i<=n+1;i++) { if(p[i].x==-1)continue; f[i]=C(p[i].x+p[i].y,p[i].x); for(int j=1;j<i;j++) f[i]=(f[i]-f[j]*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)%Mod+Mod)%Mod; } printf("%lld\n",f[n+1]); return 0; }
以上是关于[BZOJ 4767]两双手(组合数学+Dp)的主要内容,如果未能解决你的问题,请参考以下文章