算法竞赛——进阶指南——acwing213. 古代猪文 lucas定理+欧拉定理+中国剩余定理,巧妙的性质!

一道很考验数论基本功的题目,知识杂糅。

首先指数取模一般用欧拉定理降维(利用指数循环节)

而这必须满足q与mod互质,

由于mod是质数,当q==mod时,结果为0,否则q与mod互质

则可用欧拉定理的推论得到:

q^{ \sum_{d|n}C_n^d\ mod\ 999911658 }(mod\ 999911659)

然后只需处理质数部分即可。

大数的组合数用lucas定理求。

但由于求多次,且模数太大,阶乘的复杂度过高,不可直接求。

我们分解999911659发现:其有4个质因子,且质数都为1.

这种数非常特殊:对其求4次,每次分别模一个质因子。

就得到一个线性同余方程,且模数互质,满足中国剩余定理的条件,直接求即可。

这是一个很巧妙的思路,可以把模数大的数通过中国剩余定理转化为多个模数表示。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 4e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,-1,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
const int mod[6]={0,2,3,4679,35617,999911659};
ll fac[M][6];
ll qpow (ll a,ll b,ll m)
{
	ll ans=1;
	while(b)
	{
		if(b&1)ans=ans*a%m;
		a=a*a%m;
		b/=2;	
	}
	return ans;
}

ll C(ll n,ll m,ll ty)
{
	ll p=mod[ty];
//	cout<<"====  "<<n<<" "<<m<<"  "<<ty<<endl;
	if(m>n) return 0;
	return fac[n][ty]*qpow(fac[m][ty]*fac[n-m][ty]%p,p-2,p)%p;
}
ll cal(ll n,ll m,int ty)
{
//	cout<<"--"<<n<<" "<<m<<" "<<ty<<endl;
	if(m==0)return 1;
	ll p=mod[ty];
	return C(n%p,m%p,ty)*cal(n/p,m/p,ty)%p;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll d=exgcd(b,a%b,x,y);
	ll z=x;
	x=y;
	y=z-a/b*y;
	return d;
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
	ll n,q;
	cin>>n>>q;
	ll ans[5]={0};
	for(int j=1;j<=5;j++)
		for(int i=0;i<=4e5;i++)
			if(i==0)fac[i][j]=1;
			else fac[i][j]=fac[i-1][j]*i%mod[j];
	for(int i=1;i*i<=n;i++)
	{
		if(n%i)continue;
		ll d=i;
		for(int j=1;j<=4;j++)ans[j]=(ans[j]+cal(n,d,j))%mod[j];
		if(i!=n/i)
		{
			d=n/i;
			for(int j=1;j<=4;j++)ans[j]=(ans[j]+cal(n,d,j))%mod[j];
		}
	}
	/*
	x%mod[j]=ans[j]同余方程 
	*/
	ll m=999911658;
	ll tp=0;
	for(int i=1;i<=4;i++)
	{
	//	cout<<i<<"  "<<ans[i]<<endl;
		ll x,y,z;
		ll p=mod[i];
		z=m/p;
		exgcd(z,p,x,y);//gcd(z,p)==1
		x=(x%p+p)%p;
		tp+=ans[i]*z*x;
	}
	tp%=m;
	if(q==mod[5])cout<<0<<endl;
	else
	cout<<qpow(q,tp,mod[5])<<endl;
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值