Welcome to th|

XiaoLe_MC

园龄:1年2个月粉丝:3关注:9

2024-05-06 22:35阅读: 23评论: 0推荐: 0

[学习笔记] 乘性函数 - 数论

[SDOI2012] Longge 的问题

我们要求 i=1ngcd(i,n),但 gcd 没啥卵用,所以尝试给这n个正整数分组。对于 gcd(i,n)=1 的数给他们归到 G(1) 这个集合里去,当然,这个集合元素的数量为 ϕ(n)。对于 gcd(i,n)=2 的数归到 G(2) 这个集合里去。观察 G(2) 这个集合可以发现,如果把所有元素都除以2,那么他们都会和 n2 互素。那么 G(2) 这个集合的元素数量即为 ϕ(n2)……找到规律,于是,问题就转化为了:dndϕ(nd)

又因为 nnd 是一一对应的,所以也可以写成 dnndϕ(d)

考虑分解欧拉函数 ϕ(d)=d(11p1)(11p2)(11pi)

dnndϕ(d)=dnnddpidpi1pi=dnnpidpi1pi=ndnpidpi1pi

观察上式,我们知道,n=p1k1p2k2piki,并且 n 的因子 d 其实是对 n 所有素因子的排列组合。令 ti=pi1pi。当 d=piki 时,无论 ki 为多少(不等于0),pidpi1pi=ti。而且,这样的 d 一共有 ki 个,所以能够给答案的贡献为 ki×ti。同理,当 d=pikipjkj 时,对答案产生的贡献为 kikj×titj。特殊地,当 d=pi0 时贡献为0。枚举所有 d,所有的贡献加在一起即为:

ans=1+k1t1+k2t2+k1k2t1t2+=(k1t1+1)(k2t2+1)=pinkiti+1=pinpi1pi×ki+1

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

poj2478 Farey Sequence

i=2nϕ(i) 的值。进行欧拉筛的同时利用前缀和预处理即可。也可进行离线操作。

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

苦恼的小明

首先,根据欧拉定理可知:

abab mod ϕ(p)+ϕ(p) mod p

这个定理的好处就在于可以大大降低指数的大小。以下内容用到了这个狮子。

现在来观察题目:若要求 a1a2a3mod 10007 ,可以先缩小指数,求出 a2a3mod ϕ(10007) +ϕ(10007)。但还是不够,再一次缩小指数,求出 a3mod ϕ(ϕ(10007)) +ϕ(ϕ(10007))。以此类推,不断缩小指数,最后再使用快速幂取模得出答案。

很显然可以使用递归求解,并且当 a 的数量超过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;
}

上帝与集合的正确用法

image

跟 # 苦恼的小明 一个道理。

首先,根据欧拉定理可知:

abab mod ϕ(p)+ϕ(p) mod p

所以我们能做的就是不断地利用欧拉定理把指数缩小。比如要求 222 mod p,考虑缩小指数 k1=22 mod ϕ(p),接着缩小 k2=2 mod ϕ(ϕ(p))。如此类推,把得到的新指数用快速幂求解即可。

当然,不难发现,当 p!=1 时总有 ϕ(p)<p。所以总有一刻 ϕ=1 。那这就好办了,模数都等于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 中国大陆许可协议进行许可。

posted @   XiaoLe_MC  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起