洛谷 4139 上帝与几何的正确用法 题解

博客围绕求无穷个2的值对p(p<=1e7)取余数的问题展开。介绍了输入输出格式及样例,提出利用欧拉定理递归求解,设f(p)表示原结果膜p的值,f(p)=2f(ϕ(p))+ϕ(p)%p,p=1时返回0,还提及筛数范围及特殊情况处理。

题意简述

2 2 2 2 2 2 . . . 2 2^{2^{2^{2^{2^{2^{^{{...}^{2}}}}}}}} 222222...2的值(其中有无穷个2)对 p ( p &lt; = 1 e 7 ) p(p&lt;=1e7) p(p<=1e7)取余数。多组数据。

数据

输入

第一行一个 T T T,表示数据个数。接下来 T T T行,每行一个 p p p

输出

对于每个数据,给出答案。

样例

输入
3
2
3
6
输出
0
1
4

思路

正无穷个 2 2 2叠在一起诶。。。这个怎么算啊。。。

别急,我们不是还有欧拉定理呢么。我们会发现,在降幂时,我们将指数膜 ϕ ( p ) \phi(p) ϕ(p),然后膜 ϕ ϕ ( p ) \phi\phi(p) ϕϕ(p),然后膜 ϕ ϕ ϕ ( p ) ⋯ \phi\phi\phi(p)\cdots ϕϕϕ(p)一会, ϕ ( p ) \phi(p) ϕ(p)的值就变成了 1 1 1。我们知道 1 1 1膜任何数都是 0 0 0,所以虽然有无穷个 2 2 2叠在一起,但是值是有限的,并且是一个定值。

所以我们就考虑递归求解。设 f ( p ) f(p) f(p)表示原结果膜 p p p的值,显然, f ( p ) = 2 f ( ϕ ( p ) ) + ϕ ( p ) % p f(p)=2^{f(\phi(p))+\phi(p)}\%p f(p)=2f(ϕ(p))+ϕ(p)%p。当 p = 1 p=1 p=1时,我们就返回0。

但是还有一点要注意,我筛就是筛到 1 e 6 1e6 1e6,如果 p p p 1 e 6 1e6 1e6以上,就需要 O ( p ) O(\sqrt{p}) O(p )求一遍了。但是这样的过程不会进行多少次(经计算,不会超过4次)。
(当然如果你常数小的话,珂以筛到 1 e 7 1e7 1e7,就不怕 p &gt; 1 e 6 p&gt;1e6 p>1e6的时候的暴力求了)

代码:

#include<bits/stdc++.h>
#define N 10010000
using namespace std;
namespace Flandle_Scarlet
{
    int phi[N],primes[N];
    bool notp[N];
    void Init()//筛出phi
    {
        notp[1]=phi[1]=1;
        int n=10000000;
        int& cnt=primes[0];
        for(int i=2;i<=n;++i)
        {
            if (!notp[i])
            {
                primes[++cnt]=i;
                phi[i]=i-1;
            }
            for(int j=1;j<=cnt and i*primes[j]<=n;++j)
            {
                int u=primes[j];
                notp[i*u]=1;
                if (i%u==0)
                {
                    phi[i*u]=u*phi[i];
                    break;
                }
                else
                {
                    phi[i*u]=(u-1)*phi[i];
                }
            }
        }
    }

    int smul(int a,int b,int m)//防爆的龟速乘
    {
        int r=0;
        while(b)
        {
            if (b&1) r=(r+a)%m;
            a=(a<<1)%m,b>>=1;
        }
        return r;
    }
    int qpow(int a,int b,int m)//快速幂
    {
        int r=1;
        while(b)
        {
            if (b&1) r=smul(r,a,m);
            a=smul(a,a,m),b>>=1;
        }
        return r;
    }
    int calc(int mod)//那个f函数,此处写成了calc
    {
        if (mod==1) return 0;
        return qpow(2,calc(phi[mod])+phi[mod],mod)%mod;
    }

    void Query()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            int m;scanf("%d",&m);
            printf("%d\n",calc(m));
        }
    }
    void Main()
    {
        Init();
        Query();
    }
}

int main()
{
    Flandle_Scarlet::Main();
    return 0;
}

回到总笔记界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值