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; } //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!