[BZOJ Contest-2017省队十连测推广赛1·T3][BZOJ4767][DP][容斥原理]两双手

这题跟BZOJ3782 上学路径思路一样。
但是这道题的图不是网格图,这个时候就要重建一下图,首先我们发现,从起点走到一个点用的A走法和B走法的次数是固定的,假设A走法用了x次,B走法用了y次,那么可以列一个方程组:

{Axx+Bxy=xiAyx+Byy=yi

这样就可以求出从起点到 (xi,yi) 用了多少次A走法和B走法,那么把用A走法的次数想象成平面的x坐标,B走法的次数想象成y坐标,那么就可以建出一张网格图,就用BZOJ3782的做法就行了

#include 
#include 
#include 
#define N 1000010
#define p 1000000007

using namespace std;

typedef pair<int,int> pairs;
typedef long long ll;

int n,Ex,Ey,a1,a2,b1,b2,cnt;
ll f[N],fac[N],inv[N];
struct node{
  int x,y;
  friend bool operator <(node a,node b){
    return a.xx||(a.x==b.x&&a.yy);
  }
}w[N];

#define Nosol pairs(-1,-1)

inline pairs solve_eq(int A,int B){
  if((B*b1-A*b2)%(a2*b1-a1*b2)) return Nosol;
  int x=(B*b1-A*b2)/(a2*b1-a1*b2);
  if((B*a1-A*a2)%(a1*b2-a2*b1)) return Nosol;
  int y=(B*a1-A*a2)/(a1*b2-a2*b1);
  if(x<0||y<0) return Nosol;
  return pairs(x,y);
}

inline ll C(int x,int y){
  return fac[x]*inv[y]%p*inv[x-y]%p;
}

inline ll Get(int a,int b){
  int A=w[b].x-w[a].x,B=w[b].y-w[a].y;
  if(A<0||B<0) return 0;
  return C(A+B,A);
}

int main(){
  scanf("%d%d%d",&Ex,&Ey,&n);
  scanf("%d%d%d%d",&a1,&a2,&b1,&b2);
  pairs A=solve_eq(Ex,Ey);
  if(A==Nosol) return puts("0"),0;
  w[++cnt].x=A.first; w[cnt].y=A.second;
  for(int i=1,x,y;i<=n;i++){
    scanf("%d %d",&x,&y);
    pairs A=solve_eq(x,y);
    if(A!=Nosol&&A.first<=w[1].x&&A.second<=w[1].y) w[++cnt].x=A.first,w[cnt].y=A.second;
  }
  fac[0]=fac[1]=inv[0]=inv[1]=1;
  for(ll i=2;i<=600000;i++) fac[i]=fac[i-1]*i%p;
  for(ll i=2;i<=600000;i++) inv[i]=1ll*(p-p/i)*inv[p%i]%p;
  for(int i=2;i<=600000;i++) inv[i]=inv[i]*inv[i-1]%p;
  sort(w+1,w+1+cnt);
  for(int i=1;i<=cnt;i++){
    f[i]=C(w[i].x+w[i].y,w[i].x);
    for(int j=1;j*Get(j,i))%p+p)%p;
  }
  printf("%lld\n",f[cnt]);
  return 0;
}

你可能感兴趣的:(DP,容斥原理)