BZOJ4767 两双手

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

题目链接:BZOJ4767

正解:组合数学+$DP$

解题报告:

  推广赛终于AK辣

  考虑从原点出发走到某个点的所需的两种走法的次数,是唯一确定的,那么我把从原点到每个点的所需次数算出来之后,就变成了一个只能往右或者往上的常规路径计数问题了。

  这是一类有障碍的网格图计数,如果没有障碍的话,从$(0,0)$到$(n,m)$的路径条数就是$C_{n+m}^{n}$,如果有障碍的话就必须要容斥,指数级容斥显然不行,我们考虑另辟蹊径。

  先把所有关键点(障碍或者目标)排序,设$f[i]$为从原点出发到达第i个关键点且不经过其他任何一个关键点的路径条数,$g[i][j]$表示从$i$到$j$的所有路径条数,那么$f[i]=g[0][i]-\sum_{j=1}^{i-1}g[j][i]*f[j]$,含义就很明显了。

  这道题启发了我,容斥并非都是指数级的,对于这种有障碍的网格图计数显然可以通过排序和$DP$来做到$O(n^2)$。

 

//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
#include <bitset>
using namespace std;
typedef long long LL;
typedef long double LB;
const int MAXN = 1011;
const int mod = 1000000007;
const int MAXM = 1000011;
int n;
int Ex,Ey,Ax,Ay,Bx,By;
LL f[MAXN],jie[MAXM],nj[MAXM];

//有障碍点的网格图路径计数

struct node{ int x,y; }a[MAXN];
inline bool cmp(node q,node qq){ if(q.x==qq.x) return q.y<qq.y; return q.x<qq.x; }
inline LL fast_pow(LL x,LL y){ LL r=1; while(y>0) { if(y&1) r*=x,r%=mod; x*=x; x%=mod; y>>=1; } return r; }
inline LL C(LL x,LL y){ if(x<y) return 0; return jie[x]*nj[y]%mod*nj[x-y]%mod; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline void calc(int &x,int &y){
	LL a1,a2,b1,b2;
	a1=x*By-y*Bx; a2=Ax*By-Ay*Bx;
	b1=x*Ay-Ax*y; b2=Bx*Ay-Ax*By;
	if(a2==0 || b2==0) { x=-1; y=-1; return ; }
	if((a1/a2)*a2!=a1 || (b1/b2)*b2!=b1) { x=-1; y=-1; return ; }
	LL A=a1/a2,B=b1/b2;
	x=A; y=B;
}

inline void work(){
	Ex=getint(); Ey=getint(); n=getint();
	Ax=getint(); Ay=getint(); Bx=getint(); By=getint();
	calc(Ex,Ey);
	for(int i=1;i<=n;i++) {
		a[i].x=getint(),a[i].y=getint();
		calc(a[i].x,a[i].y);
		if(a[i].x<0 || a[i].y<0 || a[i].x>Ex || a[i].y>Ey)
			n--,i--;
	}
	a[0].x=a[0].y=0;
	a[++n].x=Ex; a[n].y=Ey;
	sort(a+1,a+n+1,cmp);

	int M=1000000;
	jie[0]=1; 
	for(int i=1;i<=M;i++) jie[i]=jie[i-1]*i%mod;
	nj[0]=1; nj[M]=fast_pow(jie[M],mod-2);
	for(int i=M-1;i>=1;i--) nj[i]=nj[i+1]*(i+1)%mod;

	for(int i=1;i<=n;i++) {
		f[i]=C(a[i].x+a[i].y,a[i].x);
		if(f[i]==0) continue;
		for(int j=1;j<i;j++) {
			f[i]-=(f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x))%mod;
			f[i]%=mod;
			f[i]+=mod; f[i]%=mod;
		}
	}
	printf("%lld",f[n]);
}

int main()
{
    work();
    return 0;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

posted @ 2017-03-08 17:37  ljh_2000  阅读(1221)  评论(0编辑  收藏  举报