【BZOJ 3884】上帝与集合的正确用法【欧拉降幂】

题目:

做法:
欧拉降幂: axax mod ϕ(p)+ϕ(p)(mod p) (xp)
证明贴个地址:地址虽然似乎是爬虫搞出来的但是再也找不到了原出处了.
有了欧拉降幂,可以设 f(p)=2222... mod p
带入欧拉降幂:

f(p)=222...modϕ(p)+ϕ(p)mod p

2222...p

注意到现在的指数和 f 有相同的结构,即:
f(p)=2f(ϕ(p))+ϕ(p) mod p

由此我们可以递归计算 f ,并将f(1)=0(显然任意数 mod 1=0 )作为边界即可.
时间复杂度似乎并不显然,但是注意到递归次数就是使得 ϕ(ϕ(ϕ(...ϕ(p))))=1 的迭代次数.
而有以下结论:
pϕ(p)p212=p2

p,pqi,ϕ(p)=pq11q1q21q2...qn1qn.p,qiqi1,ϕ(p),

所以,每两次递归调用时 p 至少减半,即调用次数为O(logp).加上欧拉函数的计算,总时间复杂度为 O(plogp) .

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;

namespace MYS
{
    const int MAXN = 10000005;
    typedef long long LL;

    int phi_table[MAXN];

    int phi(int x)
    {
        if(phi_table[x]) return phi_table[x];
        int &ret = phi_table[x], upper = sqrt(x + 0.5);
        ret = x;
        for(int i=2;i<=upper;i++) {
            if(x % i == 0) {
                ret /= i;
                ret *= i-1;
                while(x % i == 0) x /= i;
            }
        }
        if(x > 1) { ret /= x; ret *= x-1; }
        return ret;
    }

    LL qpow(LL a, LL n, int p)
    {
        LL ret = 1, tmp = a % p;
        while(n) {
            if(n & 1) ret = ret * tmp % p;
            tmp = tmp * tmp % p;
            n >>= 1;
        }
        return ret;
    }   

    LL f(int p)
    {
        if(p == 1) return 0;
        int phip = phi(p);
        return qpow(2, f(phip) + phip, p);
    }

    void work()
    {
        int p;
        scanf("%d", &p);
        printf("%d\n", (int)f(p));
    }

    void Main()
    {
        int T;
        scanf("%d", &T);
        while(T--) work();
    }
}


int main()
{
    freopen("in.txt", "r", stdin);
    MYS::Main();
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值