FJNU Yehan’s hole 组合数逆元+容斥

Yehan’s hole

Description

 

Yehan is a angry grumpy rabbit, who likes jumping into the hole. This day,Yehan jumps again in the pit at home. Each time, he should jump from the hole at the coordinate (1,1) to (n,m), and he has to jump as the way : he can only jump (x, y) to (x+1, y) or (x, y+1). At his home, some holes are filled with water, he couldn’t jump in them. Yehan wants to know how many different way he could jump. If the paths of two ways are different, Yehan considers they are different.

Input

 

Multiple test dataThe first line of input contains two numbers n, m (2 <= n, m < 1e5)The second line of input contains a number q, the number of holes (q <= 15)The following q lines, the i-th line contains two numbers xi, yi (1 < x1 < x2 < x3 <……< xq < n) (1 < y1 < y2 < y3 < …… < yq < m)

Output

 

Output contains one line, the number of ways, because the result is too large, you should mod 1000000007

Sample Input 1 

3 3
1
2 2

Sample Output 1

2

 

题意:从左上角(1,1)走到右下角(n,m),n*m的图中有一些格点是洞,不能走,且只能往右和向下走,问方案数。

题解:如果没有洞,那么我们只要往下或往右走,那么另外一边就可以确定了,那么总的方案数就是C(n-1,n+m-2);

 

有洞的情况,我们就可以用容斥定理,总方案数减去经过一个洞的情况,加上经过两个洞的情况,减去经过三个洞的情况。。。

注意最后答案要加上模数后再取模,因为dfs容斥过程中答案有可能出现负数。

 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#include<vector> 
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a));
#define lowbit(x)  x&-x;  
typedef long long ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 5;
struct node{
	int x,y;
}hole[mx];
int n,m,h;
ll inv[2*mx+20];
ll fac[2*mx+20];
ll ans; 
ll pow_inv(ll a,ll b){
	ll res = 1;
	while(b){
		if(b&1) res = (res*a)%mod;
		a = (a*a)%mod;
		b >>= 1;
	}
	return res;
}
void Inv(){ //阶乘逆元 
	fac[0] = 1;
	for(int i = 1; i <= 200000; i++) //阶乘打表 
		fac[i] = (fac[i-1]*i)%mod;
	inv[200000] = pow_inv(fac[200000],mod-2); //最大阶乘逆元 
	for(int i = 200000-1; i >= 0; i--)
		inv[i] = (inv[i+1]*(i+1))%mod; //递推阶乘逆元
}
ll C(ll b,ll a){
	return fac[a]*inv[b]%mod*inv[a-b]%mod;
} 
void dfs(int cur,int pre,ll temp,int s){ //当前洞,前一个洞,种类数,取了几个洞(容斥) 
	ll tmp1,tmp2;
	s++;
	tmp1 = temp*C(hole[cur].x-hole[pre].x,hole[cur].x+hole[cur].y-hole[pre].x-hole[pre].y)%mod; //前一个洞到当前洞的种类数 
	tmp2 = tmp1*C(n-hole[cur].x,n+m-hole[cur].x-hole[cur].y)%mod;   //当前洞到终点的种类数 
	if(s&1) ans = (ans + tmp2)%mod; //注意取模
	else ans = (ans - tmp2)%mod;
	for(int i = cur+1; i <= h; i++){
		dfs(i,cur,tmp1,s);
	} 
}
int main(){
	Inv();
	while(~scanf("%d%d",&n,&m)){
		scanf("%d",&h);
		hole[0].x = 1,hole[0].y = 1;
		rep(i,1,h){
			scanf("%d%d",&hole[i].x,&hole[i].y);
		}
		ans = 0;
		rep(i,1,h){
			dfs(i,0,1,0);
		}
		printf("%lld\n",((C(n-1,n+m-2)-ans)%mod+mod)%mod);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值