lucas定理 hdu5794

这篇博客详细介绍了卢卡斯定理及其在组合计数问题中的应用。通过代码实现展示了如何利用卢卡斯定理计算组合数,并给出了HDU5794题目的解决方案,该题目涉及障碍物路径计数,进一步阐述了卢卡斯定理在处理模运算中的高效性。
摘要由CSDN通过智能技术生成

定义

证明 

这里写图片描述

 代码实现

//lucas定理
//1.qpow,阶乘(init)
//2.lucas
//3.组合排列C(nm)
#include<bits/stdc++.h>
#define ll long long 
using namespace std ;
const int mod = 110119 ;
ll jc[mod+1],nc[mod+1] ;
ll qpow(ll x,int k)
{
	ll base = 1 ;
	while(k)
	{
		if(k&1) base = base*x%mod ;
		k>>=1 ;
		x = x*x%mod ;
	}
	return base ;
}
void init()
{
	jc[0] = 1 , nc[0] = 1 ;
	for(int i = 1 ; i < mod ; i++)
	{
		jc[i] = jc[i-1]*i%mod ;
		nc[i] = qpow(jc[i],mod-2) ;//费马小定理 
	}
 } 
ll compose(ll n,ll m)
{
	if(n<0 || m<0 || n<m) return 0 ;
	if(n==m) return 1 ;
	return jc[n]*nc[n-m]%mod*nc[m]%mod ;
}
ll lucas(ll n,ll m)
{
	if(n<0 || m<0 || n<m) return 0 ;
	ll ans = 1 ;
	while(n||m)
	{
		ans = ans*compose(n%mod,m%mod)%mod ;
		n/=mod ;
		m/=mod ;
	}
	return ans ;
}
int main()
{
	init() ;
	ll n,m ;
	while(~scanf("%lld%lld",&n,&m))
	{
		printf("%lld\n",lucas(n,m)) ;
	}
	return 0 ;
}

例题

hdu5794

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std ;
const int mod = 110119 ;
const int N = 105 ;
//思路:1.算出s1,s2,tot=s1+s2 ; C(s1,tot)即为总方案数
//2.减去障碍可以到达的方案数就ok
//3.对于当前点,f[i]=ans-障碍点可以到达的总方案数
//所以要对每个障碍数从小到大,算出其路径数,才可以 
struct node{
	ll x,y ;
	bool operator < (node &b) const//小的排前边 
	{
		if(x==b.x) return y<b.y ;
		return x<b.x ;
	}
}a[N];
ll f[N],jc[mod+1],ni[mod+1] ;
ll qpow(ll x,int k)
{
	ll base = 1 ;
	while(k)
	{
		if(k&1) base = base*x%mod ;
		k >>= 1 ;
		x = x*x%mod ;
	}
	return base ;
}
void init()
{
	jc[0] = 1 , ni[0] = 1 ;
	for(int i = 1 ; i < mod ; i++)
	{
		jc[i] = 1ll*jc[i-1]*i%mod ;
		ni[i] = qpow(jc[i],mod-2) ;
	}
 } 
int compus(int n,int m)
{
	if(n<0 || m<0 || n<m) return 0 ;
	return 1ll*jc[n]*ni[m]%mod*ni[n-m]%mod ;
}
int lucas(ll n,ll m)
{
	//lucas定理 a = ∑ak*mod^k , b = ∑bk*mod^k
	//C(a,b)=C(ak,bk)*C(ak-1,bk-1)*...*C(a1,b1)
	if(n<m || n<0 ||m<0 ) return 0 ;
	
	int ans = 1 ;
	while(n||m)
	{
		ans = 1ll*ans*compus(n%mod,m%mod)%mod ;
		n/=mod ;
		m/=mod ;
	 } 
	return ans ;
}
int main()
{
	ll n,m ;
	int t,kase=0 ;
	init() ;
	while(~scanf("%lld%lld%d",&n,&m,&t))
	{
		for(int i = 1 ; i <= t ; i++) scanf("%lld%lld",&a[i].x,&a[i].y) ;
		a[++t]= (node){n,m} ;
		sort(a+1,a+t+1) ;
		memset(f,0,sizeof(f)) ;
		
		for(int i = 1 ; i <= t ; i++)
		{
		    ll s = a[i].x+a[i].y-2 ;
		    if(s%3) continue ; //总步数s%3=0才可以走到该点 
		    s/=3 ;
		    //ll s1 = a[i].x-1-s ;//向x方向走的步数
		    ll s1 = (2ll*a[i].x-a[i].y-1)/3 ;
			f[i] = lucas(s,s1) ;//C(s1,s) 
			//printf("x=%d y=%d s=%d s1=%d f[%d]=%lld \n",a[i].x,a[i].y,s,s1,i,f[i]) ;
			
			for(int j = 1 ; j < i ; j++)//正确算出i点的路径数 
			{
				if(a[j].x>a[i].x || a[j].y>a[i].y) continue ;
			    ll ss = a[i].x-a[j].x+a[i].y-a[j].y ;
			    if(ss%3) continue ;
			    ss/=3 ;//总步数 
			    
			    //ll s2 = a[i].x-a[j].x-ss ;//向x方向走的步数
			    ll s2 = (2ll*(a[i].x-a[j].x)-(a[i].y-a[j].y))/3 ; 
			    //printf("x=%d y=%d ss=%d s2=%d lucas=%lld \n",a[j].x,a[j].y,ss,s2,lucas(ss,s2)) ;
				f[i] = (f[i]-1ll*f[j]*lucas(ss,s2)%mod+mod)%mod ;
			 } 
		}
		printf("Case #%d: %d\n",++kase,f[t]) ;
	}
	return 0 ;
}

参考:(9条消息) 卢卡斯定理(十分钟带你看懂)_Combatting 的博客-CSDN博客_卢卡斯定理(9条消息) hdu5794_weixin_34071713的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值