bzoj 1951 [Sdoi2010]古代猪文

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83474404

这是一道数论集合题

求式子g^{\sum_{m|n} C_{n}^{m}} \ mod \ 999911659,首先可以先用欧拉定理将式子降幂为指数mod (999911659-1)。

而对于指数可以在根号n的时间内枚举每个约数,对于每个约数求相应的组合数,可以用lucas定理来求解,

但是因为这个模数不是指数,所以把这个模数拆开,再用中国剩余定理合并就好了

999911658=2*3*4679*35617

注意的点是当g等于999911659时,这个定理不适用,但显然答案是0,所以要特判掉。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define mode 999911658
using namespace std;
typedef long long ll;
ll n,g,x,y;
ll mod[5]={0,2,3,4679,35617};
ll niv[5][40005],jc[5][40005],ans[5],tot;
void gett(int u)
{
	niv[u][1]=1;
	for(int i=2;i<mod[u];i++)
	{
		niv[u][i]=(mod[u]-mod[u]/i)*niv[u][mod[u]%i]%mod[u];
	}
	jc[u][0]=1;
	for(int i=1;i<mod[u];i++)
	{
		jc[u][i]=jc[u][i-1]*i%mod[u];
	}
	niv[u][0]=1;
	for(int i=1;i<mod[u];i++)
	{
		niv[u][i]=niv[u][i-1]*niv[u][i]%mod[u];
	}
}
ll exgcd(ll a,ll b)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll r=exgcd(b,a%b);
	ll t=x;
	x=y;
	y=t-a/b*x;
	return r;
}
ll Lucas(ll x,ll y,int u)
{
	if(x==0)return 1;
	if(y%mod[u]<x%mod[u])return 0;
	return Lucas(x/mod[u],y/mod[u],u)%mod[u]*jc[u][y%mod[u]]%mod[u]*niv[u][x%mod[u]]%mod[u]*niv[u][((y-x)%mod[u]+mod[u])%mod[u]]%mod[u];
}
ll crt(int u)
{
	ll as=0;
	for(int i=1;i<=4;i++)
	{
		ans[i]=Lucas(u,n,i);
		ll gg=exgcd(mode/mod[i],mod[i]);
		ans[i]/=gg;
		x=(x%mod[i]+mod[i])%mod[i];
		x*=ans[i]%mode;x%=mode;
		as+=x*mode/mod[i]%mode;
		as%=mode;
	}
	return as;
}
void ys(ll u)
{
	for(ll i=1;i*i<=u;i++)
	{
		if(u%i==0)
		{
			tot+=crt(i)%mode;
			tot%=mode;
			if(i*i==u)continue;
			tot+=crt(u/i)%mode;
			tot%=mode;
		}
	}
}
ll ksm(ll x,ll y)
{
	ll as=1;
	while(y)
	{
		if(y%2==1)
		{
			as=as*x%(mode+1);
		}
		x=x*x%(mode+1);
		y=y/2;
	}
	return as;
}
int main()
{
	scanf("%lld%lld",&n,&g);
	if(g==mode+1)
	{
		printf("0");
		return 0;
	}
	for(int i=1;i<=4;i++)gett(i);
	ys(n);
	printf("%lld",ksm(g,tot)%(mode+1));
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/83474404