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

来源:互联网 发布:域名交易平台php源码 编辑:程序博客网 时间:2024/05/03 17:55

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

{Axx+Bxy=xiAyx+Byy=yi

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

#include <cstdio>#include <iostream>#include <algorithm>#define N 1000010#define p 1000000007using 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.x<b.x||(a.x==b.x&&a.y<b.y);  }}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<i;j++)      f[i]=((f[i]-f[j]*Get(j,i))%p+p)%p;  }  printf("%lld\n",f[cnt]);  return 0;}
0 0
原创粉丝点击