字符串+数论(扩展欧拉定理)

题意:给定一个整数k和mod,给定若干个仅有数字和小写字母构成的字符串,对于每一个字符串,将其看成一个p进制数('a'看做10...'z'看做35,故p<=36),然后求出其对应的十进制数tot,然后求(k^{tot})%mod.

分析:本题将用到快速幂模板,扩展欧拉定理以及分解质因数法求欧拉函数(卖个广告关于欧拉函数及定理,无证明)

首先暴力思路不难想到,直接按照题意一步步求,但是要知道最大是长度为(10^5)的36进制数,即便开了long long,也能够轻松爆掉.我们的问题在于,无法将一个长度为(10^5)的36进制数转化为long long类型?

目前我只知道两种做法,第一种是根据二进制快速幂巧妙设计出p进制快速幂(假设是求(x^e)):

qp(ll x,ll e,ll p) {
	ll res=1;
	while(e){
		取出e的最后一位t,去掉e的最后一位;
    	res*=x^t;
    	x=x^p;
	}
	return res;
}

我写的是第二种做法,我们现在是要求(k^{tot}),因为在求tot时会爆long long,于是要用到扩展欧拉定理:当a,n不一定互质,且b>=φ(n)时,有(a^b≡a^{b mod φ(n)+φ(n)})(mod n),所以我们在求tot时,就可以一边求一边对φ(mod)取模,这样tot就不会爆long long了.

于是可以先用分解质因数法求出phi(mod),然后求tot时对phi(mod)取模,最后求(k^{tot}+phi(mod))

int shu[100005];
char ch[100005];
int jz(char s){
    if(s>='0'&&s<='9')return s-'0';
    else return s-'a'+10;
}
ll qp(ll a,ll b,ll c){
    ll res=1;
    while(b){
		if(b&1)res=(res*a)%c;
		a=(a*a)%c;
		b=b/2;
    }
    return res%c;
}//快速幂模板
int phi(int x){
    int y=x;
    for(int i=2;i*i<=x;i++){
		if(x%i==0){
	    	y=y/i*(i-1);
	    	while(x%i==0)x/=i;
		}
    }
    if(x>1)y=y/x*(x-1);
    return y;
}//分解质因数法求欧拉函数
int main(){
    int k=read(),mod=read();
    while(~(scanf("%s",ch))){
		int len=strlen(ch),maxn=0,pos=-1;
		for(int i=0;i<len;i++){
	    	shu[i]=jz(ch[i]);
	    	if(shu[i]>maxn)maxn=shu[i];
	    	if(pos==-1&&shu[i]!=0)pos=i;
//这个pos是为了避开前导零,节约时间,但是好像也没必要
		}
		++maxn;//进制
		ll tot=0;
		int cnt=-1,res=phi(mod);
		for(int i=len-1;i>=pos;i--){
	    	++cnt;
	    	int now=shu[i];
	    	if(!now)continue;//是零就跳过不求,也没什么必要
	    	tot+=now*qp(maxn,cnt,res);
		}
		printf("%lld
",qp(k,tot+res,mod));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/10359864.html