超级乘方【修正版】

博客探讨了超级乘方的计算方法,特别是在模运算下的乘方置换规律。作者指出原算法存在的问题,并针对特定情况进行了修正,如考虑序列中出现1的影响。更新后的算法经过数据对拍,解决了快速幂的等价判断和边界问题。此外,还提及了一个相关的数论问题:计算在0~m中满足i^(i!)%p==b的i数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我表示我完全是看题解莫名其妙对了,题目和代码暂存在这里。

如有大牛路过,欢迎留评论

【a^b≡a^(b mod phi(c)+phi(c))  (mod c)(b>=phi(c))】【公式的条件很苛刻,原以为(b>=phi(c))没有意义】

其中phi(c) 是欧拉函数

原题目:

 

一、超级数

【题目描述】:

众所周知,多次加法运算是乘法,多次乘法运算是乘方,那么多次乘方运算呢?比如:

2^2=4

2^2^2=16

2^2^2^2=65536

现在我需要你的力量来计算出:

a1^a2^a3^a4^a5^...an mod p

但是,这个数光是想想就远远超过了整个宇宙的大小,所以你只要求出它的值MOD P就可以了

 

【输入】:

第一行两个数NP,如题目描述。

第二行N个数,表示a1~n

 

【输出】:

仅一个数,表示它的值mod p的结果。

 

【样例输入】:

5 13

2 2 2 2 2

 

【样例输出】:

3

 

【说明】:

20%的数据N3ai<1000

40%的数据aip为质数或1

100%的数据N20aipMaxlongint

原题解: 挖掘乘法运算在取摸下的置换规律

 

(a^b^c^...) mod mo >>> [a^( (b^c^...) mod phi( mo / gcd(mo,a^x) ) ) ] mod mo

{x 只要稍微大一点就可以了,梁盾取的是43,学号==}

既可以递归为子问题。

另外有几个特殊情况,具体见标程。

fgm中+i是个细节
对于求phi,据罗雨屏解释(函数来自梁盾)(感谢袁珺提问),
phi(n)=n∏(p-1)/p;p是n的质因数。
对于n分解质因数,n=p1^a1*p2^a2*p3^a3...*pn^an.
将phi式展开,phi(n)=p1^(a1-1)*...*pn^(an-1)*(p1-1)*...(pn-1);
每次第一次遇到pi,phi:=phi*(pi-1);之后不停从n中分解pi,phi:=phi*pi;
【递归处理时,如果小于phi(c),就直接快速幂,否则就用公式,注意在快速幂时判断是否可用公式】
【用c++重写了一遍,原来pascal版好像有bug(gcd那里估计只是出错概率比较低罢了),而且也不适合多组数据】

 c++新版:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
const int maxu=10000000;
using namespace std;
int phi[maxu+1],n,T,tot,Prim[maxu/10],flag;
long long p,a[21],ans;
bool check[maxu];
long long fgm(long long b,long long e,long long mo)
{
    long long sum=1;
    int lag=0;
    for (;e;e>>=1) {
        if (e&1) {
            if (flag || b>mo || lag || sum>mo) {
                sum=(sum*b)%mo;
                flag=lag=1;
            }
            else sum=sum*b;
        }
        if (lag || b>mo) {
            lag=1;
            b%=mo,b=(b*b)%mo;
        }
        else b=b*b; 
    }
    return sum;
}
long long pow_pow(int x,int p)
{
    if (x==n) return a[x];
    long long b=pow_pow(x+1,phi[p]);
    if (flag || b>=phi[p]) {
        flag=1;
        b=(b%phi[p])+phi[p];
        return fgm(a[x],b,p);
    }
    else return fgm(a[x],b,p);
}
void origin()
{
    phi[1]=1;
    tot=0;
    for (int i=2;i<=maxu;i++) {
        if (!check[i]) Prim[++tot]=i,phi[i]=i-1;
        for (int j=1;j<=tot;j++) {
            if (Prim[j]*i>maxu) break;
            check[Prim[j]*i]=1;
            if (i%Prim[j]==0) {
                phi[Prim[j]*i]=phi[i]*Prim[j];
                break;
            }
            else {
                phi[Prim[j]*i]=phi[i]*phi[Prim[j]];
            }
        }
    }
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
    cin>>T;
    origin();
    for (;T;T--) {
        cin>>n>>p;
        for (int i=1;i<=n;i++) cin>>a[i];
        flag=0;
        ans=pow_pow(1,p)%p;
        cout<<ans<<endl;
    }
	return 0;
}



updata1:asdfsfasf2同学后来提了一个反例把我这个程序cha掉了,是算2^1^3 % 4,因为我的程序中找到第一个ai^ai+1^...an >=phi[pi]的时候,就默认以后的运算的次方都会大于等于phi[pj],但是如果序列中有1的话就会出错,这里我现在特判掉了,但不知道还有没有其他问题,所以新程序暂时先不放上来


updata2:又鼓捣了一下,现在目测应该没什么问题了,用三种方式生成数据对拍了很久,以前主要问题在于,1、如果序列中有1的话,使用公式的区间就不能从后面开始确定;2、快速幂的时候,没考虑到判断等于的情况,以及暴界的可能。

然后对于使用公式的区间判定,因为受高一时写的程序的影响,采取从后往前递推判断,除了1之外,不知道有没有其他特例,所以我又写了一遍裸判的,不过貌似原来的判断方式也没有拍出错来。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
const int maxu=10000000;
using namespace std;
int phi[maxu+1],n,T,tot,Prim[maxu/10],flag;
long long p,a[21],ans;
bool check[maxu];
long long fgm(long long b,long long e,long long mo,int o)
{
    long long sum=1;
    for (;e;e>>=1) {
        if (e&1) {
            if (o && (b>=mo || sum>=mo || sum*b>=mo)) return -1;
            else sum=sum*b%mo;
        }
        if ((b>=mo || b*b>=mo) && o) return -1;
        else b=b*b%mo; 
    }
    return sum;
}
long long checkp(int x,long long p)
{
	if (x==n) return (a[x]>=p) ? -1 : a[x];
	long long ans=a[n];
	for (int i=n-1;i>=x;i--) {
		ans=fgm(a[i],ans,p,1);
		if (ans<0) return -1;
	}
	return ans;
} 
long long pow_pow(int x,long long p)
{
    if (x==n) return a[x];
    long long flag=checkp(x+1,phi[p]);
    if (flag<0) {
    	long long b=pow_pow(x+1,phi[p]);
    	b=(b%phi[p])+phi[p];
    	return fgm(a[x],b,p,0)%p;
    }
    else {
    	return fgm(a[x],flag,p,0)%p;
    }
}
void origin()
{
    phi[1]=1;
    tot=0;
    for (int i=2;i<=maxu;i++) {
        if (!check[i]) Prim[++tot]=i,phi[i]=i-1;
        for (int j=1;j<=tot;j++) {
            if (Prim[j]*i>maxu) break;
            check[Prim[j]*i]=1;
            if (i%Prim[j]==0) {
                phi[Prim[j]*i]=phi[i]*Prim[j];
                break;
            }
            else {
                phi[Prim[j]*i]=phi[i]*phi[Prim[j]];
            }
        }
    }
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout); 
    cin>>T;
    origin();
    for (;T;T--) {
        cin>>n>>p;
        for (int i=1;i<=n;i++) cin>>a[i];
        for (int i=n;i>=1;i--)
        	if (a[i]==1) n=i;
        flag=0;
        ans=pow_pow(1,p)%p;
        cout<<ans<<endl;
    }
	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
const int maxu=10000000;
using namespace std;
int phi[maxu+1],n,T,tot,Prim[maxu/10],flag;
long long p,a[21],ans;
bool check[maxu];
long long fgm(long long b,long long e,long long mo)
{
    long long sum=1;
    int lag=0;
    for (;e;e>>=1) {
        if (e&1) {
            if (flag || b>=mo || lag || sum>=mo || sum*b>=mo) {
                sum=(sum*b)%mo;
                flag=lag=1;
            }
            else sum=sum*b;
        }
        if (lag || b>=mo || b*b>=mo) {
            lag=1;
            b%=mo,b=(b*b)%mo;
        }
        else b=b*b; 
    }
    return sum;
}
long long pow_pow(int x,int p)
{
    if (x==n || a[x]==1) return a[x];
    long long b=pow_pow(x+1,phi[p]);
    if (flag || b>=phi[p]) {
    		flag=1;
        b=(b%phi[p])+phi[p];
        return fgm(a[x],b,p);
    }
    else return fgm(a[x],b,p);
}
void origin()
{
    phi[1]=1;
    tot=0;
    for (int i=2;i<=maxu;i++) {
        if (!check[i]) Prim[++tot]=i,phi[i]=i-1;
        for (int j=1;j<=tot;j++) {
            if (Prim[j]*i>maxu) break;
            check[Prim[j]*i]=1;
            if (i%Prim[j]==0) {
                phi[Prim[j]*i]=phi[i]*Prim[j];
                break;
            }
            else {
                phi[Prim[j]*i]=phi[i]*phi[Prim[j]];
            }
        }
    }
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output2.txt","w",stdout); 
    cin>>T;
    origin();
    for (;T;T--) {
        cin>>n>>p;
        for (int i=1;i<=n;i++) cin>>a[i];
        flag=0;
        ans=pow_pow(1,p)%p;
        cout<<ans<<endl;
    }
	return 0;
}



hdu4335

计算在0~m中有多少个i满足i^(i!)%p==b

i!<phi[p]显然很少,然后至多到phi[p],指数就不会变了,相当于(i%p)^(phi[p])%p==b,显然i%p有循环节。m==2^64-1时是trick

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define ll unsigned long long
using namespace std;
ll ans,b,p,m;
int v[200000],check[200000],phi[200000],prim[200000],tot,T;
ll fgm(ll b,ll e,ll mo)
{
    b%=mo;
    ll sum=1;
    for (;e;e>>=1,b=(b*b)%mo) 
        if (e&1) sum=(sum*b)%mo;
    return sum;
}
void origin()
{
    tot=0,phi[1]=1;
    for (int i=2;i<=100000;i++) {
        if (!check[i]) prim[++tot]=i,phi[i]=i-1;
        for (int j=1;j<=tot;j++) {
            if (i*prim[j]>100000) break;
            check[i*prim[j]]=1;
            if (i%prim[j]) phi[i*prim[j]]=phi[i]*phi[prim[j]];
            else {
                phi[i*prim[j]]=phi[i]*prim[j];
                break;
            }
        }
    }
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
    cin>>T;
    origin();
    for (int t=1;t<=T;t++) {
        cin>>b>>p>>m;
        if (1==p && 0==b) {
            if (m!=18446744073709551615LLU) cout<<"Case #"<<t<<": "<<m+1<<endl;
            else cout<<"Case #"<<t<<": 18446744073709551616"<<endl;
            continue;
        }
        ll sum,i,j,k;
        ans=0;
        for (i=0,sum=1;sum<phi[p] && i<=m;++i,sum*=i) 
            if (fgm(i,sum,p)==b) ans++;
        for (sum%=phi[p];sum && i<=m;++i,sum=(sum*i)%phi[p])
            if (fgm(i,sum+phi[p],p)==b) ans++;
        if (i<=m) {
            ll cnt=0;
            for (j=i;i<=m && (v[i%p]!=t);i++) {
                ll tmp=fgm(i,phi[p],p);
                if (tmp==b) cnt++;
                v[i%p]=t;
            }
            if (i<=m) {
                k=i-j;
                ans+=(m-j+1)/k*cnt;
                if ((m-j+1)%k) {
                    i=j+(m-j+1)/k*k;
                    for (;i<=m;i++)
                        if (fgm(i,phi[p],p)==b) ans++;
                }
            }
            else ans+=cnt;
        }
        cout<<"Case #"<<t<<": "<<ans<<endl;
    }
	return 0;
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值