• 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;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    C#-获取页面源代码
    C#-获取页面源代码
    C#-窗体移动
    C#-窗体移动
    C#-窗体鼠标穿透
    C#-窗体鼠标穿透
    C#-string生成图片
    C#-string生成图片
    C#-Stmp发邮件
    POJ-1611 The Suspects
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6520511.html
Copyright © 2020-2023  润新知