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

On Iteration of One WellKnown FunctionOn\ Iteration\ of\ One\ Well-Known\ Function


Description\mathcal{Description}
给出 n=i=1mpiain=\prod\limits_{i=1}^mp_i^{a_i}, 求 φ(φ(...φ(n)))φ(φ(...φ(n))) .

m105,pi106,ai1017,k1018.m\le10^5, p_i\le10^6, a_i \le 10^{17}, k \le 10^{18} .


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

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

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

从小到大共有10610^6个质数, 对每个质数单独处理,
设当前质数为 pip_i, 还需进行 leftileft_iφ\varphi 操作, 指数为 aia_i,

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


对当前 pip_i, 若进行了 ttphiphi 操作, 则需要在指数为 00 时, SOSi=t1SOS_i = t-1 次操作不去减 leftileft_i, 因为可能还有指数不为 00 的希望,
过了 SOSiSOS_i 后若指数仍然为 00, 表示没救了, 需要使 leftileft_i 减小 .


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

记得在开始时每个质数的 leftleft 都为 KK, 因为每个质数都有可能参与运算 .

#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;
}

posted @ 2019-07-15 11:08  XXX_Zbr  阅读(316)  评论(0编辑  收藏  举报