【题解】[SDOI2010]古代猪文

【题目描述】

猪王国的文明源远流长,博大精深。

i P i g iPig iPig 在大肥猪学校图书馆中查阅资料,得知远古时期猪文文字总个数为 n n n。当然,一种语言如果字数很多,字典也相应会很大。当时的猪王国国王考虑到如果修一本字典,规模有可能远远超过康熙字典,花费的猪力、物力将难以估量。故考虑再三没有进行这一项劳猪伤财之举。当然,猪王国的文字后来随着历史变迁逐渐进行了简化,去掉了一些不常用的字。

i P i g iPig iPig 打算研究古时某个朝代的猪文文字。根据相关文献记载,那个朝代流传的猪文文字恰好为远古时期的 1 / k 1/k 1/k,其中 k k k n n n 的一个正约数(可以是 1 1 1 n n n)。不过具体是哪 1 / k 1/k 1/k,以及 k k k 是多少,由于历史过于久远,已经无从考证了。

i P i g iPig iPig 觉得只要符合文献,每一种 k ∣ n k|n kn 都是有可能的。他打算考虑到所有可能的 k k k。显然当 k k k 等于某个定值时,该朝的猪文文字个数为 n / k n/k n/k。然而从 n n n 个文字中保留下 n / k n/k n/k 个的情况也是相当多的。 i P i g iPig iPig 预计,如果所有可能的 k k k 的所有情况数加起来为 p p p 的话,那么他研究古代文字的代价将会是 g p g^p gp

现在他想知道猪王国研究古代文字的代价是多少。由于 i P i g iPig iPig 觉得这个数字可能是天文数字,所以你只需要告诉他答案除以 999911659 999911659 999911659 的余数就可以了。

【输入格式】

一行两个正整数 n , g n,g n,g

【输出格式】

输出一行一个整数表示答案。

【输入输出样例】

输入

4 2

输出

2048

【数据规模与约定】

  • 对于 10 % 10\% 10% 的数据, 1 ≤ n ≤ 50 1\le n \le 50 1n50
  • 对于 20 % 20\% 20% 的数据, 1 ≤ n ≤ 1000 1\le n \le 1000 1n1000
  • 对于 40 % 40\% 40% 的数据, 1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1n105
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n , g ≤ 1 0 9 1\le n,g \le 10^9 1n,g109

【算法分析】

本题实际上就是求 q ∑ d ∣ n C n d m o d   999911659 q^{\sum_{d|n}C_n^d}mod\ 999911659 qdnCndmod 999911659 d d d n n n 的约数。

根据欧拉定理:若 a , n a,n a,n 互质,对于任意的整数 b b b ,有 a b ≡ a b   m o d   φ ( n ) ( m o d e   n ) a^b\equiv a^{b\ mod\ \varphi (n)}(mode\ n) abab mod φ(n)(mode n) φ ( n ) \varphi (n) φ(n) 是欧拉函数。

q = 999911659 q=999911659 q=999911659,则上式结果为 0 0 0 ,否则,因为 999911659 999911659 999911659 是质数,与任何数互质,因此:

q ∑ d ∣ n C n d ≡ q ∑ d ∣ n C n d   m o d   999911658 ( m o d   999911659 ) q^{\sum_{d|n}C_n^d}\equiv q^{\sum_{d|n}C_n^d\ mod \ 999911658}(mod\ 999911659) qdnCndqdnCnd mod 999911658(mod 999911659)

现在就转换为求解 q q q 的次方: ∑ d ∣ n C n d   m o d   999911658 \sum_{d|n}C_n^d\ mod \ 999911658 dnCnd mod 999911658

因为模数较大,若直接使用 L u c a s Lucas Lucas 定理会超时。尝试将模数缩小。

9999116589 9999116589 9999116589 质因数分解,可得 999911658 = 2 × 3 × 4679 × 35617 999911658=2\times 3\times 4679\times 35617 999911658=2×3×4679×35617,可以使用 L u c a s Lucas Lucas 定理计算组合数 ∑ d ∣ n C n d \sum_{d|n}C_n^d dnCnd 2 , 3 , 4679 , 35617 2,3,4679,35617 2,3,4679,35617 分别为 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4,在计算过程中,对于质数 p p p ,可以预处理出 1 ! ∼ p ! 1!\sim p! 1!p! p p p ,以及阶乘模 p p p 的逆元,可以快速求解出组合数模 p p p 的值。

那么 ∑ d ∣ n C n d   m o d e   999911658 \sum_{d|n}C_n^d\ mode \ 999911658 dnCnd mode 999911658 的解 x x x 满足:

{ x ≡ a 1 ( m o d   2 ) x ≡ a 2 ( m o d   3 ) x ≡ a 2 ( m o d   4679 ) x ≡ a n ( m o d   35617 ) \left\{\begin{matrix} x\equiv a_1(mod\ 2) \\ x\equiv a_2(mod\ 3) \\ x\equiv a_2(mod\ 4679)\\ x\equiv a_n(mod\ 35617) \\ \end{matrix}\right. xa1(mod 2)xa2(mod 3)xa2(mod 4679)xan(mod 35617)

得到 x x x 的最小正整数解,最后的答案就是 q x q^x qx ,可以使用快速幂求解。

本题是数论的综合应用,欧拉定理+ L u c a s Lucas Lucas 定理+乘法逆元+中国剩余定理+快速幂 。

【参考程序】

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
ll divv[N],cnt;
ll KSM(ll x,ll k,ll P)			//快速幂 
{
	ll ans=1;
	while(k)
	{
		if(k&1)	ans=(ans*x)%P;
		k>>=1;
		x=(x*x)%P;
	}
	return ans%P;
}
//组合数C(n,m)=n!/(m!*(n-m)!)=n!/(n-m)!取模乘以m!模P的逆元
//P是质数,可以使用费马小定理 
ll C(ll n,ll m,ll P)	 
{	
	ll a=1,b=1;
	for(int i=n-m+1;i<=n;i++)	a=(a*i)%P;
	for(int i=2;i<=m;i++)	b=(b*i)%P;
	return (a*KSM(b,P-2,P))%P;	//费马小定理求逆元 
} 
ll Lucas(ll x,ll y,ll P)
{
	if(!y) return 1; 
	if(x<y) return 0;
	if(x<P&&y<P)	return C(x,y,P);
	return (Lucas(x%P,y%P,P)*Lucas(x/P,y/P,P))%P;
}
int main()  
{  
	ll n,g;
	scanf("%lld%lld",&n,&g);
	if(g%999911659==0) {cout<<0;return 0;} 
	for(int i=1;i*i<=n;i++)			//找约数 
	{
		if(i*i==n) divv[++cnt]=n/i;
		else if(n%i==0)	divv[++cnt]=i,divv[++cnt]=n/i;
	}
	ll a1=0; 
	for(int i=1;i<=cnt;i++)
		a1=(a1+Lucas(n,divv[i],2))%2;
	ll a2=0; 
	for(int i=1;i<=cnt;i++)
		a2=(a2+Lucas(n,divv[i],3))%3;
	ll a3=0; 
	for(int i=1;i<=cnt;i++)
		a3=(a3+Lucas(n,divv[i],4679))%4679;	
	ll a4=0; 
	for(int i=1;i<=cnt;i++)
		a4=(a4+Lucas(n,divv[i],35617))%35617;
	ll P=999911658;
	//中国剩余定理,由于2,3,679,35617是质数,可以直接使用费马小定理求解 
	ll ans=(a1*P/2*KSM(P/2,2-2,2))%P+(a2*P/3*KSM(P/3,3-2,3))%P+(a3*P/4679*KSM(P/4679,4679-2,4679))%P+(a4*P/35617*KSM(P/35617,35617-2,35617))%P;
	ans=((ans%P)+P)%P;		//取最小正整数 
	cout<<KSM(g,ans,P+1)<<endl;
	return 0;  
}  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值