CF396E On Iteration of One Well-Known Function [欧拉函数 III]

O n   I t e r a t i o n   o f   O n e   W e l l − K n o w n   F u n c t i o n On\ Iteration\ of\ One\ Well-Known\ Function On Iteration of One WellKnown Function


D e s c r i p t i o n \mathcal{Description} Description
给出 n = ∏ i = 1 m p i a i n=\prod\limits_{i=1}^mp_i^{a_i} n=i=1mpiai, 求 φ ( φ ( . . . φ ( n ) ) ) φ(φ(...φ(n))) φ(φ(...φ(n))) .

m ≤ 1 0 5 , p i ≤ 1 0 6 , a i ≤ 1 0 17 , k ≤ 1 0 18 . m\le10^5, p_i\le10^6, a_i \le 10^{17}, k \le 10^{18} . m105,pi106,ai1017,k1018.


正 解 部 分 正解部分
因为 φ ( n ) = n ∏ i = 1 m p i − 1 p i \varphi(n)=n\prod\limits_{i=1}^m\dfrac{p_i-1}{p_i} φ(n)=ni=1mpipi1,
所以对一个整数 x x x 进行一次 φ \varphi φ 操作即 ↓ ↓

  • 乘一次 ∏ i = 1 m 1 p i \prod\limits_{i=1}^{m}\frac{1}{p_i} i=1mpi1, 即 p i p_i pi项的幂数减 1 1 1 .
  • 乘一次 ∏ i = 1 m ( p i − 1 ) \prod\limits_{i=1}^{m}(p_i-1) i=1m(pi1), 对 p i − 1 p_i-1 pi1 分解质因数, 使得对应的较小的质数的幂数增加若干.

那么怎么快速地去求解本题的式子呢?

从小到大共有 1 0 6 10^6 106个质数, 对每个质数单独处理,
设当前质数为 p i p_i pi, 还需进行 l e f t i left_i lefti φ \varphi φ 操作, 指数为 a i a_i ai,

为保证效率最大化, 将 p i p_i pi 进行 t = m i n ( a i , l e f t i ) t=min(a_i, left_i) t=min(ai,lefti) φ \varphi φ 操作,
这样会使得 ( p i − 1 ) t (p_i-1)^t (pi1)t 分解, “散落” 到比 p i p_i pi 更小的质数中,
所以需要循环往复地对每个质数进行这个操作, 直到没有任何质数有操作的余地 .


对当前 p i p_i pi, 若进行了 t t t p h i phi phi 操作, 则需要在指数为 0 0 0 时, S O S i = t − 1 SOS_i = t-1 SOSi=t1 次操作不去减 l e f t i left_i lefti, 因为可能还有指数不为 0 0 0 的希望,
过了 S O S i SOS_i SOSi 后若指数仍然为 0 0 0, 表示没救了, 需要使 l e f t i left_i lefti 减小 .


实 现 部 分 实现部分
在质数筛时记录每个数 x x x 的最小质因子 c o m e [ x ] come[x] come[x], 分解质因数时, 就可以利用 c o m e [ x ] come[x] come[x] 直接分解 .

记得在开始时每个质数的 l e f t left left 都为 K K K, 因为每个质数都有可能参与运算 .

#include<cstdio>
#include<algorithm>
#define reg register
typedef long long ll;

const int maxn = 1e6+5;

int M;
int p_cnt;
int p[maxn];
int q[maxn];
int Mp[maxn];
int come[maxn];

ll K;
ll a[maxn];
ll SOS[maxn];
ll left[maxn];

void Sieve(){
        for(reg int i = 2; i < maxn; i ++){
                if(!come[i]) come[i] = i, p[++ p_cnt] = i, Mp[i] = p_cnt;
                for(reg int j = 1; j <= p_cnt && i*p[j] < maxn; j ++){
                        int t = i*p[j];
                        come[t] = p[j];
                        if(i % p[j] == 0) break ;
                }
        }
}

int main(){
        //freopen("a.out", "w", stdout);
        Sieve();
        scanf("%d", &M);
        for(reg int i = 1; i <= M; i ++){
                scanf("%d", &q[i]);
                scanf("%I64d", &a[Mp[q[i]]]);
        }
        scanf("%I64d", &K);
        for(reg int i = 1; i <= p_cnt; i ++) left[i] = K;
        bool flag = 1;
        while(flag){
                flag = 0;
                for(reg int i = 1; i <= p_cnt; i ++)
                        if(a[i]){ 
                                if(!left[i]) continue ;
                                ll t = std::min(a[i], left[i]);
                                a[i] -= t, left[i] -= t;
                                SOS[i] += t-1, flag = 1;
                                int tmp = p[i]-1;
                                while(tmp != 1){
                                        int id = Mp[come[tmp]];
                                        a[id] += t;
                                        tmp /= come[tmp];
                                }
                        }else if(SOS[i]) SOS[i] --;
                        else if(left[i])left[i] --;
        }
        int Ans = 0;
        for(reg int i = 1; i <= p_cnt; i ++) if(a[i]) Ans ++;
        printf("%d\n", Ans);
        for(reg int i = 1; i <= p_cnt; i ++)
                if(a[i]) printf("%d %I64d\n", p[i], a[i]);
        return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值