【日常学习】【组合数取模Lucas定理】HDU3037 Saving Beans题解

15 篇文章 0 订阅
7 篇文章 0 订阅

【提前声明:此题没有通过!WA!有待进一步研究修改。放在这里只是起一个例子的作用,其实这道题鄙人并没有真正掌握= =】、

【本文努力抄袭模仿了小花妹妹的博文0戳我0)】

  • 题目大意:共T个测试点,每个测试点中,给定n、m,求将不超过m个种子放入n个坑的方案总数,最后答案对质数p取模。(一共m个,每个坑放多少无所谓,最后没放完m个也无所谓)

  • 数据范围:1 <= n, m <= 1000000000, 1 < p < 100000。

  • 思路:原题意即求方程x1+…+xn=m解的个数,因为中间的每一项均可为0,故两面同时将加上n,转换成为x1+…+xn+n=m+n,即x1’+…+xn’=m+n,其中每一项至少为1,则由隔板法可知,解的个数为C(n+m-1,m),这只是恰好为m个的方案数,总数为ans=C(n-1,0)+C(n,1)+…+C(n+m-1,m)=C(n+m,m)。(注意,这里组合数的意思表示为:n+m选m个)

  • 知识储备:

    • (A * B) mod C = (A mod C) * (B mod C) mod C
    • Lucas定理:记Lucas(n,m,p)=C(n,m) mod p,则Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)。
    • 乘法逆元:假如p是质数,且a、p互质,那么a的(p-1)次方除以p的余数恒为1,那么a和a^(p-2)互为乘法逆元,则(b / a) = (b * a^(p-2) ) mod p。
  • 注意:中途必须强制转换类型,否则出错。【但没有人知道为什么而且最后还是挂了】

鄙人代码:


//HDU3037 Saving Beans Lucas¶¨Àí×éºÏÊýÈ¡Ä£ 
//copyright by ametake

#include<iostream>
using namespace std;

typedef long long ll;

ll t,n,m,p;

ll multi(ll a,ll b)
{
	ll ans=1;
	while (b>0)
	{
		if (b & 1 == 1) ans=((ll)ans*(ll)a)%p;
		a =( (ll)a * (ll)a ) % p;
		b =(ll)(b << 1); 
	}
	return ans;
}

ll c(ll n,ll m)
{
	ll a=1,b=1;
	if (m>n) return 0;
    while (m)
    {
    	a=(a*n)%p;
    	b=(b*m)%p;
    	m--;
    	n--;
    	
	}
	return ((ll)a*(ll)multi(b,p-2))%p;
}

ll lucas(ll n,ll m,ll p)
{
	if ( !m ) return 1;
	else return ( (ll)lucas(n/p,m/p,p)*(ll)c(n%p,m%p)&p);
}

int main()
{
	cin>>t;
	for (ll i=1;i<=t;i++)
	{
		cin>>n>>m>>p;
		cout<<lucas(n+m,m,p)<<endl;
	}
	return 0;
}
 

由于各种时间紧迫,悲剧的用了自带代码粘贴 各种悲剧 下次再改改吧

下一站就要转战DP海洋了···挣扎的小鱼啊···

但无论如何古诗词是不能少的 


——凄凉别后两应同,最是不胜清怨月明中。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值