# BZOJ4767：两双手 （组合数学+DP+容斥原理）

$f\left[i\right]=g\left(0,i\right)-\sum _{j.x\le i.x,j.y\le i.y}f\left[j\right]\ast g\left(j,i\right)$

CODE：

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=550;
const int maxm=1000000;
const long long M=1000000007;
typedef long long LL;

struct data
{
int X,Y;
} point[maxn];
LL f[maxn];
int cnt;

LL fac[maxm];
LL nfac[maxm];

int tx,ty;
int ax,ay,bx,by,tp;
int n;

bool Work(int &u,int &v)
{
int q=u*ay-v*ax;
if ( !tp || q%tp ) return false;
q/=tp;
int p;
if (ax)
{
p=u-q*bx;
if (p%ax) return false;
p/=ax;
}
else
{
if (!ay) return false;
p=v-q*by;
if (p%ay) return false;
p/=ay;
}
if ( p<0 || q<0 ) return false;
u=p;
v=q;
return true;
}

bool Comp(data x,data y)
{
return x.X<y.X || ( x.X==y.X && x.Y<y.Y );
}

LL C(int nn,int mm)
{
LL val=fac[nn];
val=val*nfac[nn-mm]%M;
val=val*nfac[mm]%M;
return val;
}

int main()
{
freopen("hands.in","r",stdin);
freopen("hands.out","w",stdout);

scanf("%d%d%d",&tx,&ty,&n);
scanf("%d%d%d%d",&ax,&ay,&bx,&by);

tp=bx*ay-by*ax;
if ( !Work(tx,ty) )
{
printf("0\n");
return 0;
}

for (int i=1; i<=n; i++)
{
int u,v;
scanf("%d%d",&u,&v);
if ( !Work(u,v) ) continue;
if ( u>tx || v>ty ) continue;
cnt++;
point[cnt].X=u;
point[cnt].Y=v;
}

cnt++;
point[cnt].X=tx;
point[cnt].Y=ty;
sort(point+1,point+cnt+1,Comp);

fac[0]=1;
for (LL i=1; i<maxm; i++) fac[i]=fac[i-1]*i%M;
nfac[0]=nfac[1]=1;
for (LL i=2; i<maxm; i++)
{
LL x=M/i,y=M%i;
nfac[i]=M-x*nfac[y]%M;
}
for (int i=1; i<maxm; i++) nfac[i]=nfac[i-1]*nfac[i]%M;

for (int i=1; i<=cnt; i++)
{
f[i]=C(point[i].X+point[i].Y,point[i].X);
for (int j=1; j<i; j++)
{
int dx=point[i].X-point[j].X;
int dy=point[i].Y-point[j].Y;
LL temp=0;
if ( dx>=0 && dy>=0 ) temp=f[j]*C(dx+dy,dx)%M;
f[i]=(f[i]-temp+M)%M;
}
}
printf("%I64d\n",f[cnt]);

return 0;
}

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客