「SDOI 2010」古代猪文

传送门


problem

给定 n , g n,g n,g,求 g ∑ d ∣ n C n d g^{\sum_{d|n}C_n^d} gdnCnd 999911659 999911659 999911659 取模的结果。

数据范围: 1 ≤ n , g ≤ 1 0 9 1 \le n,g \le 10^9 1n,g109


solution

考虑直接算指数,我们要求的是 ∑ d ∣ n C n d \sum_{d|n}C_n^d dnCnd

根据费马小定律降幂,我们要对 999911658 999911658 999911658 取模,它不是质数,不存在逆元,怎么办呢?

对其质因数分解: 999911658 = 2 × 3 × 4679 × 35617 999911658=2\times3\times4679\times35617 999911658=2×3×4679×35617

那么就比较显然了,我们可以先分别求出以这几个质数为模数的答案,最后 CRT \texttt{CRT} CRT 合并答案。

由于这几个模数都比较小,可以用 Lucas 定理在 O ( log ⁡ n ) O(\log n) O(logn) 的时间内求组合数。

然后就 O ( n ) O(\sqrt n) O(n ) 枚举约数算答案即可。

时间复杂度 O ( n log ⁡ n ) O(\sqrt n \log n) O(n logn)


code

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5,mod=999911659;
int P,fac[N],ifac[N],a[4],p[4]={2,3,4679,35617};
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
void exgcd(int a,int b,int &x,int &y){
	if(!b)  {x=1,y=0;return;}
	exgcd(b,a%b,y,x),y-=a/b*x;
}
int C(int n,int m)  {return n<m?0:mul(fac[n],mul(ifac[m],ifac[n-m]));}
int Lucas(int n,int m){
	if(n<P&&m<P)  return C(n,m);
	return mul(Lucas(n/P,m/P),C(n%P,m%P));
}
void prework(){
	fac[0]=fac[1]=1;
	for(int i=2;i<P;++i)  fac[i]=mul(fac[i-1],i);
	ifac[P-1]=power(fac[P-1],P-2);
	for(int i=P-2;~i;--i)  ifac[i]=mul(ifac[i+1],i+1);
}
int CRT(int ans=0){
	P=mod-1;
	for(int i=0;i<4;++i){
		int x,y,now=P/p[i];
		exgcd(now,p[i],x,y),x=(x+p[i])%p[i];
		ans=add(ans,mul(a[i],mul(x,now)));
	}
	return ans;
}
int n,g;
int main(){
	scanf("%d%d",&n,&g);
	if(g==mod)  return puts("0"),0;
	for(int j=0;j<4;++j){
		P=p[j],prework();
		for(int i=1;i*i<=n;++i)if(n%i==0){
			a[j]=add(a[j],Lucas(n,i));
			if(i*i!=n)  a[j]=add(a[j],Lucas(n,n/i));
		}
	}
	int exp=CRT();
	P=mod,printf("%d\n",power(g,exp));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值