zoukankan      html  css  js  c++  java
  • [BZOJ4767]两双手

    bzoj

    description

    一个(n imes m)的棋盘上,一匹马有且仅有两种走法,分别对应向量((A_x,A_y))((B_x,B_y)),保证两向量不共线。有(n)个障碍点((x_i,y_i)),马不能跳到障碍点上,求从((0,0))走到((n,m))的方案数模(10^9+7)

    sol

    先假设没有障碍。
    由于两向量不共线,所以马要从((0,0))跳到((n,m)),两种走法分别需要的步数是确定的。这个可以列出方程求解,注意如果解不是整数则说明无解。
    如果第一种走法需要(x)步,第二种走法需要(y)步,那么方案数显然就是(inom{x+y}{x})
    有了障碍点,那么一定就需要容斥。指数级的容斥显然是不行的,需要寻求更高效率的方法。
    我们设(f[i])表示从((0,0))出发不经过任何障碍点到达第(i)个障碍点的方案数,设(g[i][j])表示从第(i)个障碍点随意走到第(j)个障碍点(允许经过中间的障碍点)的方案数。这里要先把所有障碍点排序。
    这样就存在转移:(f[i]=g[0][i]-sum_{j=1}^{i-1}f[j] imes g[j][i])(假设第(0)个障碍点是((0,0))),也就是枚举第一个经过的障碍点是哪个,然后就可以轻松地做到(O(n^2))的转移。
    令第(n+1)一个障碍点是((n,m)),那么答案就是(f[n+1])了。
    注意因为(A_x,A_y,B_x,B_y)可以有负数,所以所需的步数可能是(O(n^2))级别的,需要注意预处理组合数的上界。

    code

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    const int N = 1000005;
    const int mod = 1e9+7;
    int Ex,Ey,n,Ax,Ay,Bx,By,inv[N],jc[N],jcn[N],f[N];
    struct node{
    	int x,y;
    	bool operator < (const node &b) const{
    		return x==b.x?y<b.y:x<b.x;
    	}
    }a[N];
    void work(int &x,int &y){
    	int a1=x*By-y*Bx,a2=Ax*By-Ay*Bx;
    	int b1=x*Ay-y*Ax,b2=Ay*Bx-Ax*By;
    	if (!a2||!b2) {x=y=-1;return;}
    	if (a1/a2*a2!=a1||b1/b2*b2!=b1) {x=y=-1;return;}
    	x=a1/a2;y=b1/b2;
    }
    int C(int n,int m){
    	return 1ll*jc[n]*jcn[m]%mod*jcn[n-m]%mod;
    }
    int cal(int x,int y){
    	if (x<0||y<0) return 0;
    	return C(x+y,y);
    }
    int main(){
    	Ex=gi();Ey=gi();n=gi();
    	Ax=gi();Ay=gi();Bx=gi();By=gi();
    	work(Ex,Ey);if (Ex<0&&Ey<0) return puts("0"),0;
    	for (int i=1;i<=n;++i){
    		a[i].x=gi(),a[i].y=gi();work(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[++n]=(node){Ex,Ey};sort(a+1,a+n+1);
    	jc[0]=jcn[0]=inv[0]=inv[1]=1;
    	for (int i=2;i<N;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    	for (int i=1;i<N;++i) jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=1ll*jcn[i-1]*inv[i]%mod;
    	for (int i=1;i<=n;++i){
    		f[i]=cal(a[i].x,a[i].y);
    		for (int j=1;j<i;++j)
    			f[i]=(f[i]-1ll*f[j]*cal(a[i].x-a[j].x,a[i].y-a[j].y)%mod+mod)%mod;
    	}
    	printf("%d
    ",f[n]);return 0;
    }
    
  • 相关阅读:
    315. 计算右侧小于当前元素的个数
    55. 跳跃游戏
    <leetcode c++>72. 编辑距离
    "NTLDR is missing"和"NTLDR is compressed"的解决办法
    Windows Live Mail不能发送图片附件的2种解决方法
    【】使用word2010同步更新自己在不同网站的博客
    新浪博客测试
    【】引用 CSS行高line-height属性理解及应用
    "NTLDR is missing"和"NTLDR is compressed"的解决办法
    排序算法-冒泡排序(javascript)
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/9255001.html
Copyright © 2011-2022 走看看