嘘~ 正在从服务器偷取页面 . . .

洛谷P4139 上帝与集合的正确用法 题解


洛谷P4139 上帝与集合的正确用法 题解

题目链接:P4139 上帝与集合的正确用法

题意

有无穷数列

当 $n \to +\infty$ 时,计算

可以证明 $a_n\bmod p$在 $n$ 足够大时为常数

多组数据 $1\le Q\le 10^3$ ,$1 \le p\le 10^7$

其实这个题很简单

根据扩展欧拉定理,有

显然当 $n$ 足够大时,有 $a_{n-1} > \varphi(p)$

可以发现原问题转化为了求解

递归处理即可

关于这个递归为什么是 $O(\log p)$

显然每一次递归 $p$ 都会变成 $\varphi(p)$

根据欧拉函数的公式

设 $n=\prod_{i=1}^{s}p_i^{k_i}$

观察函数,可以发现

  • 当 $n$ 为偶数时,一定会把 $2$ 提出来变成 $1$ ,也就是至少除以 $2$
  • 当 $n$ 为奇数时,一定会把一个素因子提出来变成一个偶数,也就是产生了 $2$ 这个因子
  • 重复以上的过程直到 $n=1$ 结束

因此是 $O(\log p)$ 的

所以总的时间复杂度为 $O(Q\sqrt{p}\log p)$

不用线性筛跑得反而快,因为 $p$ 蛮大的,用了就变成 $O(p+Q\log p)$ 了

数比较大,快速幂可能会爆了 $\text{long long}$ ,所以要用龟速乘或者__int128

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
int phi(int n)
{
    int ans=n;
    for(int i=2; i<=n/i; i++)
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)n/=i;
        }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}
int qpow(int a,int b,int p)
{
    int ans=1,base=a%p;
    while(b)
    {
        if(b&1)ans=(__int128)ans*base%p;
        base=(__int128)base*base%p;
        b>>=1;
    }
    return ans;
}
int solve(int p)
{
    if(p==1)return 0;
    return qpow(2,solve(phi(p))+phi(p),p);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int Q,p;
    cin >> Q;
    while(Q--)
    {
        cin >> p;
        cout << solve(p) << endl;
    }
    return 0;
}

用筛法求 $\varphi(p)$ 跑了 1.52s,非筛法反倒就跑了114ms


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录