BZOJ4767 两双手

BZOJ4767 两双手

题面:BZOJ

解析

容斥水题。先解出走到结束点和禁止点的步数(不难发现这个步数是唯一的)。然后去掉绝对不会走到的禁止点,现在考虑容斥去除禁止点的影响,不难想到+-1的容斥方法,但是怎么确定贡献呢?对于至少走\(i\)个禁止点,方案数就是强制走\(i\)个点的方案数,其后再随意分配即可。这样做很难做,不妨变换枚举顺序,改为枚举每一个点作为最后一个被走到的禁止点,这样可以\(O(n^2)\)的预处理方案数,在中途+-1即可。我也不知道我是怎么乱搞过的

代码


#include<cstdio>
#include<algorithm>
#define N 505
using namespace std;
const int P=1e9+7,__=1e6;
inline int In(){
	char c=getchar(); int x=0,ft=1;
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
	for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
	return x*ft;
}
inline int max(int a,int b){
	return a>b?a:b;
}
int Ex,Ey,Ax,Ay,Bx,By,A,B,t[N],n,lim,qc=0,ans=0,g[N],fac[__],inv[__];
struct Q{
	int a,b;
	bool operator < (const Q& t) const { return a==t.a?b<t.b:a<t.a; }
}q[N];
inline int power(int x,int k){
	int s=1,t=x;
	for(;k;k>>=1,t=1ll*t*t%P) if(k&1) s=1ll*s*t%P;
	return s;
}
inline void Sol(int x,int y,int& t1,int& t2){
	if((Bx*y-By*x)%(Ay*Bx-Ax*By)==0&&(Ax*y-Ay*x)%(By*Ax-Bx*Ay)==0)
	t1=(Bx*y-By*x)/(Ay*Bx-Ax*By),t2=(Ax*y-Ay*x)/(By*Ax-Bx*Ay);
	else t1=2e9,t2=2e9;
}
inline int f(int i,int j){
	return 1ll*fac[i+j]*inv[i]%P*inv[j]%P;
}
int main(){
	Ex=In(); Ey=In(); n=In(); Ax=In(); Ay=In(); Bx=In(); By=In();
	Sol(Ex,Ey,A,B); if(A==2e9){ printf("%d\n",0); return 0;}
	lim=A+B; fac[0]=1; for(int i=1;i<=lim;++i) fac[i]=1ll*fac[i-1]*i%P;
	inv[lim]=power(fac[lim],P-2); for(int i=lim-1;~i;--i) inv[i]=1ll*inv[i+1]*(i+1)%P;
	for(int i=1,a,b,Sx,Sy;i<=n;++i){
		Sx=In(); Sy=In(); Sol(Sx,Sy,a,b);
		if(a<=A&&b<=B) q[++qc].a=a,q[qc].b=b;
	}
	n=qc; sort(q+1,q+1+n);
	for(int i=1;i<=n;++i){
		g[i]=f(q[i].a,q[i].b);
		for(int j=1;j<i;++j) if(q[j].a<=q[i].a&&q[j].b<=q[i].b)
		g[i]=(g[i]-1ll*g[j]*f(q[i].a-q[j].a,q[i].b-q[j].b)%P+P)%P;
	}
	ans=f(A,B);
	for(int i=1,sum;i<=n;++i)
	ans=(ans+1ll*(P-1)*f(A-q[i].a,B-q[i].b)%P*g[i]%P)%P;
	printf("%d\n",ans);
	return 0;
}

posted @ 2019-03-25 21:58  pkh68  阅读(89)  评论(0编辑  收藏  举报