【SDOI2010】古代猪文(数论杂烩)

难题,这题要用到很多数论知识但都不深,有专门的知识点我就用红色标出。

(PS.洛谷上SDOI2010跟Pig有关的好多黑题,深表同情...)

首先搬出问题:

                                               G^{\sum_{d|n}{C_n^d} } \mod\ 999911659

扩展欧拉定理

                                              a^b\ mod\ p=a^{b\ mod \ \Phi (p)}\ mod\ p

而根据欧拉函数的定义,当p为质数时,

                                                         \Phi(p)=p-1

所以问题变形:

                                            =G^{\sum_{d|n}{C_n^d}\ mod\ 999911658 } \ mod\ 999911659

最外一层可以用快速幂,那么现在关键就在于求解:

                                                   \sum_{d|n}{C_n^d}\ mod\ 999911658 

我们对999911658进行质因数分解,发现999911658=2*3*4679*35617。根据取模运算的性质,这一式子可以化为:

                \sum_{d|n}{C_n^d}\ mod\ 2+\sum_{d|n}{C_n^d}\ mod\ 3+\sum_{d|n}{C_n^d}\ mod\ 4679+\sum_{d|n}{C_n^d}\ mod \35617

于是我们可以枚举n的因数d,这只需要根号n的时间,每枚举到一个d就分别计算C_n^d对四个数取模的结果,结果对应地加在a[1~4]上。而由于这四个数又各自是质数,所以可以应用卢卡斯定理,式子可化为:

\sum_{d|n}{C_{n\ mod\ 2}^{d\ mod\ 2}}*C_{n\div2}^{d\div2}\ mod\ 2+\sum_{d|n}{C_{n\ mod\ 3}^{d\ mod\ 3}}*C_{n\div3}^{d\div3}\ mod\ 3+\sum_{d|n}{C_{n\ mod\ 4679}^{d\ mod\ 4679}}*C_{n\div4679}^{d\div4679}\ mod\ 4679+\sum_{d|n}{C_{n\ mod\ 35617}^{d\ mod\ 35617}}*C_{n\div35617}^{d\div35617}\ mod\ 35617  

尽管已经化简了很多,但是每次组合数重新计算阶乘耗时巨大,因此需要预处理出35617以内的每个数的阶乘对999911658取模结果(打个比方,20既然能被10整除,也必然能被10的因数整除)。然后在计算组合数时单独求m!和(n-m)!的逆元即可。

得到对上面四个数取模的结果后,因为我们最终要找一个数能代替

                                                  \sum_{d|n}{C_n^d}\ mod\ 999911658

所以我们需要求出一个最小的x,满足:

                                                 \left\{\begin{matrix} x\equiv a[1]\ (mod\ 2)\\ x\equiv a[2]\ (mod\ 3)\\ x\equiv a[3]\ (mod\ 4679)\\ x\equiv a[4]\ (mod\ 35617) \end{matrix}\right.

由于所有的模数互质,因此这是一个简单的中国剩余定理,最后的x对999911658取模,并保留非负整数解。

整个过程中,逆元中国剩余定理扩展欧几里得算法解决。 

最后既然最棘手的解决了,剩下的就是快速幂了。记得此时的取模对象为999911659。

注意坑点:

1,中间过程long long。

2,当G=999911659,输出0即可。

3,最后快速幂时取模对象是999911659,之前的都是999911658。

#include<cstdio>
using namespace std;
typedef long long LL;
const int MAXN=35617;
int N,G,fact[MAXN+5],mod=999911658;
int prime[5]={0,2,3,4679,35617},Mod[5];

void get_fact()
{
	fact[0]=1;
	for(int i=1;i<=MAXN;i++)
		fact[i]=(LL)fact[i-1]*i%mod;
}

int ex_t;
void exgcd(int a,int b,int &x,int &y)
{
	if(!b) {x=1;y=0;return;}
	exgcd(b,a%b,x,y);
	ex_t=x; x=y; y=ex_t-(a/b)*y;
}

int inv(int a,int p)
{
	int x,y;
	exgcd(a,p,x,y);
	return (x%p+p)%p;
}

int calc(int i,int p)
{
	int ret=1,x,y,n,m;
	for(x=N,y=i;y;x/=p,y/=p)
	{
		n=x%p; m=y%p;  //卢卡斯定理+预处理阶乘+逆元 
		ret=(LL)ret*fact[n]%p*inv(fact[m],p)%p*inv(n<m?0:fact[n-m],p)%p;
	}
	return ret;
}

LL qkpow(int x,int b)
{
	int s=1;
	while(b)
	{
		if(b&1) s=(LL)s*x%mod;
		x=(LL)x*x%mod;
		b>>=1;
	}
	return s;
}

int main()
{
	scanf("%d%d",&N,&G);
	if(G%(mod+1)==0)  //坑点 
	{
		printf("0");
		return 0;
	}
	get_fact();  //得到阶乘取模结果 
	for(int i=1;i*i<=N;i++)  //枚举因数 
	{
		if(N%i==0)
		{
			for(int j=1;j<=4;j++) Mod[j]=(Mod[j]+calc(i,prime[j]))%prime[j];
			if(i*i!=N)
				for(int j=1;j<=4;j++) Mod[j]=(Mod[j]+calc(N/i,prime[j]))%prime[j];
		}
	}
	int x,y,b=0;
	for(int i=1;i<=4;i++)  //中国剩余定理 
	{
		exgcd(mod/prime[i],prime[i],x,y);
		b=(b+(LL)Mod[i]%mod*(mod/prime[i])%mod*x%mod)%mod;
	}
	b=(b%mod+mod)%mod; mod+=1; //记得mod+1 
	printf("%lld",qkpow(G,b));
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/82378151