[学习笔记] 乘性函数 - 数论
我们要求 ,但 没啥卵用,所以尝试给这n个正整数分组。对于 的数给他们归到 这个集合里去,当然,这个集合元素的数量为 。对于 的数归到 这个集合里去。观察 这个集合可以发现,如果把所有元素都除以2,那么他们都会和 互素。那么 这个集合的元素数量即为 ……找到规律,于是,问题就转化为了:。
又因为 和 是一一对应的,所以也可以写成 。
考虑分解欧拉函数
观察上式,我们知道,,并且 的因子 其实是对 所有素因子的排列组合。令 。当 时,无论 为多少(不等于0),。而且,这样的 一共有 个,所以能够给答案的贡献为 。同理,当 时,对答案产生的贡献为 。特殊地,当 时贡献为0。枚举所有 ,所有的贡献加在一起即为:
#include<bits/stdc++.h> using namespace std; #define int long long int n, ans; signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; ans = n; for(int i=2; i*i<=n; ++i){ if(n%i == 0){ int cn = 0; while(!(n%i)) n /= i, ++cn; ans /= i; ans *= i*cn - cn + i; } } if(n != 1){ ans /= n; ans *= n + n - 1;} return cout<<ans, 0; }
求 的值。进行欧拉筛的同时利用前缀和预处理即可。也可进行离线操作。
#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e6 + 1; int phi[N], pr[N], cnt, n, sum[N]; bitset<N> vis; signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); for(int i=2; i<=N; ++i){ if(!vis[i]){ pr[cnt++] = i; phi[i] = i - 1; } for(int j=0; j<cnt; ++j){ if(i*pr[j] > N) break; vis[i*pr[j]] = 1; if(i%pr[j] == 0){ phi[i*pr[j]] = phi[i] * pr[j]; break;} phi[i*pr[j]] = phi[i] * phi[pr[j]]; } sum[i] = sum[i-1] + phi[i]; } while(cin>>n && n) cout<<sum[n]<<'\n'; return 0; }
首先,根据欧拉定理可知:
这个定理的好处就在于可以大大降低指数的大小。以下内容用到了这个狮子。
现在来观察题目:若要求 ,可以先缩小指数,求出 。但还是不够,再一次缩小指数,求出 。以此类推,不断缩小指数,最后再使用快速幂取模得出答案。
很显然可以使用递归求解,并且当 的数量超过13的时候, 值就变成了1,再往上走就没有啥意义了,直接退出。当然,也可以预先求解出所有的 值,不多,就13个。
#include<bits/stdc++.h> using namespace std; int n, mod[13] = {10007, 10006, 5002, 2400, 640, 256, 128, 64, 32, 16, 8, 4, 2}; inline int getans(int times, int m){ if(!times || m >= 13) return 1; int a, ans = 1, k; cin>>a; k = getans(times-1, m+1); if(a == 1) return 1; // 注意这里 while(k){ if(k & 1) ans = (long long)ans * a % mod[m]; a = (long long)a * a % mod[m]; k >>= 1; } return m?ans + mod[m]:ans; } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; return cout<<getans(n, 0), 0; }
跟 # 苦恼的小明 一个道理。
首先,根据欧拉定理可知:
所以我们能做的就是不断地利用欧拉定理把指数缩小。比如要求 ,考虑缩小指数 ,接着缩小 。如此类推,把得到的新指数用快速幂求解即可。
当然,不难发现,当 时总有 。所以总有一刻 。那这就好办了,模数都等于1了那再往下递归就没有意义了,所以直接返回1。
#include<bits/stdc++.h> int p, t; inline int rd(){ int x = 0; char ch = getchar(); while(!isdigit(ch)) ch = getchar(); while(isdigit(ch)) x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); return x; } inline void wt(int k){ if(k/10>0) wt(k/10); putchar(k%10+'0'); } inline int getans(int mod){ if(mod == 1) return (mod==p)?0:1; //特判p=1的情况 int phi = mod, q = mod; for(int i=2; i*i<=q; ++i){ if(!(q%i)){ phi /= i, phi *= i - 1; while(!(q%i)) q /= i; } } if(q != 1) phi /= q, phi *= q - 1; //求出mod的phi值 int k = getans(phi), ans = 1, a = 2; //继续计算下一个指数 while(k){ //快速幂 if(k & 1) ans = (long long)ans * a % mod; a = (long long)a * a % mod; k >>= 1; } return (mod==p)?ans:ans+mod; } int main(){ t = rd(); while(t--){ p = rd(); int q = p; while(!(q%2)) q >>= 1; //特判是否p只有一个等于2的质因子 可要可不要 if(q == 0) putchar('0'), putchar('\n'); else wt(getans(p)), putchar('\n'); } return 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18176111
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步