洛谷P3726 [AH2017/HNOI2017]抛硬币 [扩展Lucas,中国剩余定理,卡常]

链接

一道丧心病狂的卡常题,又教会了我许多卡常技巧

首先,我们可以推一下答案的式子:

自己去看吧

当a==b时:   $$ANS=\frac{2^{a+b}-C_{2a}^{a}}{2}$$

否则:ANS=\frac{2^{a+b}+\sum_{i=1}^{a-b-1}C_{a+b}^{b+i}}{2}

接下来,就是把模板打上,然而。。。T了!

注意到模数为10^n,发现可以分解为2^n\cdot5^n,那么中国剩余定理由循环变为了一个式子

而且,扩展lucas的模数只有两个

扩展Lucas中最耗时的是什么?求阶乘!

如果模数只有两个,那么。。。

预处理!

于是,我们的lucas跑得飞快

还有一个问题:除以二怎么办?膜10^n意义下2没有逆元

我们发现如果 a+b 是奇数,那么我们需要计算的式子其实是杨辉三角中间对称的几列,那么我们只计算左边的几列或者右边的几列就可以除2了,但是在a+b为偶数的时候我们会发现剩下了一个 C_{a+b}^{(a+b)/2}情况会有点棘手

但是我们注意到根据帕斯卡恒等式(杨辉三角)可以得到

C_{2a}^{a}=C_{2a-1}^{a}+C_{2a-1}^{a-1}=2C_{2a-1}^{a}

于是我们计算 C_{a+b-1}^{(a+b)/2}​ 就可以了

                                                 ————转自https://www.luogu.org/blog/ShadowassIIXVIIIIV/solution-p3726

其实吧,还有一个地方要卡

在Lucas中计算 2 / 5 的数量时,若发现它≥k,就可以直接返回0啦

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll M;
ll ksm(ll x,ll y,ll P)
{
	ll ret=1;
	while (y)
	{
		if (y&1) ret=ret*x%P;
		x=x*x%P;
		y>>=1;
	}
	return ret;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
	ll d=a;
	if (b) d=exgcd(b,a%b,y,x),y-=a/b*x;
	else x=1,y=0;
	return d;
}
ll inv(ll t,ll p){ll x,y;exgcd(t,p,x,y);return (x%p+p)%p;}
ll f[2][2000000];
void init(ll p,ll pr)
{
	bool c=(p!=2);f[c][0]=1;
	for (ll i=1;i<=pr;i++)
	{
		f[c][i]=f[c][i-1];
		if (i%p) f[c][i]=f[c][i]*i%pr;
	}
}
ll calc(ll n,ll p,ll pr)
{
	if (!n||n==1) return 1ll;
	return ksm(f[p!=2][pr-1],n/pr,pr)*f[p!=2][n%pr]%pr*calc(n/p,p,pr)%pr;
}
ll a,b,K;
ll C(ll n,ll m,ll p,ll pr)
{
	ll i,j,k,x=0;
	if (n==m) return 1;
	if (n<m) return 0;
	for (i=p;i<=n;i*=p) x+=n/i-m/i-(n-m)/i;
	if (x>K) return 0; // !!!!!
	i=calc(n,p,pr);j=inv(calc(m,p,pr),pr);k=inv(calc(n-m,p,pr),pr);
	return i*j%pr*k%pr*ksm(p,x,pr)%pr;
}
ll pr2,pr5,inv_2,inv_5;
ll CRT(ll n,ll m){return C(n,m,2,pr2)*pr5%M*inv_5+C(n,m,5,pr5)*pr2%M*inv_2%M;}
int nb[22];
inline void print(ll x,ll k)
{
    for(int i=1;i<=10;i++)nb[i]=0;
    for(int i=1;x;i++,x/=10)nb[i]=x%10;
    for(int i=k;i>=1;i--)printf("%d",nb[i]);printf("\n");
}
int main()
{
	init(2,512); init(5,1953125);
	while (~scanf("%lld %lld %lld",&a,&b,&K))
	{
		ll ans=0;
		M=ksm(10,K,(ll)1e10);pr2=1<<K;pr5=M/pr2;
		inv_2=inv(pr2,pr5),inv_5=inv(pr5,pr2);
		if (!(a^b)) ans=((ksm(2,a+b-1,M)-CRT(a*2-1,a))%M+M)%M;
		else
		{
			for (ll i=1;i<=(a-b-1)>>1;i++) ans+=CRT(a+b,i+b),ans%=M;
			if ((a+b-1)&1) ans+=CRT(a+b-1,(a+b)>>1);
			ans%=M;
			ans+=ksm(2,a+b-1,M);
		}
		print(ans%M,K);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值