「学习笔记」初等数论

前言

更熟悉的阅读体验?

前置知识(这个应该很显然):lcm(a,b)=abgcd(a,b)

线性筛素数

点击查看代码
bool vis[MAXN]; vector<int> prm; long long ans; void sieve(){ for(int i=2;i<=n;i++){ if(!vis[i]) prm.push_back(i); for(auto j:prm){ if(i*j>n) break; vis[i*j]=true; if(i%j==0) break; } } }

例题:B3716 分解质因子 3

不等式方程(exgcd)

形如 ax+by=c 的方程(a,b 为整数),方程有解当且仅当 gcd(a,b)c

欧几里得算法:gcd(a,b)=gcd(b,amodb)

ax+by=gcd(a,b) 求解:

a|b 时,ax+by=gcd(a,b) 解为 {x=0y=1

ax+by=gcd(a,b)=gcd(b,amodb)=bx1+(amodb)y1=bx1+(aab×b)y1=bx1+(aab×b)y1=ay1+b(x1ab×y1)

{x=y1y=x1ab×y1

可以递归求解。

递归函数

点击查看代码
void gg(int &x,int &y,int a,int b){ if(a%b==0){ x=0,y=1; }else{ int xx,yy; gg(xx,yy,b,a%b); x=yy;y=xx-a/b*yy; } }

裴蜀定理

(写于 2023.4.14)这个是后补的。QWQ

定义

a,b 是不全为零的整数,则存在整数 x,y , 使得 ax+by=gcd(a,b)

然后就发现前面的式子求的就是裴蜀定理的式子。

然后写一个我自己发现的东西:(只不过我看过的文章都没讲过,可能是因为为太简单了

ax+by=c 的解,其中 a,b 为整数且 gcd(a,b)|c

首先求出 ax+by=gcd(a,b) 的解,设解为 {x=xy=y

再设 k=cgcd(a,b),则 ax+by=c 的解为 {x=kxy=ky

然后就没了。QWQ

进一步的结论可以去下面的链接。

裴蜀定理

同余

概念

对两个整数 ab,如果它们除以 d 的余数相同,则称它们
d 同余
,记作:

ab(modd)

一个小知识

ak×b=rar(modb)

k 为任意整数)

欧拉函数

φ 就是这个玩意。

定义

欧拉函数 φ(n) 表示小于等于 n(其实等不等于 n 无所谓,因为 n 一定不与 n 互质)且与 n 互质的正整数(与 n 互质即对于一个数 igcd(i,n)=1 或者说是 ni)的个数。

特别的,φ(1)=1

顺便提一嘴,1 与任何数都互质

显然, 对于一个正整数 aa 是质数当且仅当 φ(a)=a1

性质

积性:如果对于任意两个正整数 a,b,如果 gcd(a,b)=1(即 ab),φ(a×b)=φ(a)×φ(b)

欧拉反演:dnφ(d)=n。(dnF(d) 表示把 n 以内所有能整除 n 的数 d 带入 F(d) 中求和)

性质三:对于任意质数 pφ(pk)=pkpk1

性质三证明:小于等于 pk 的数有 pk 个,其中 p,2×p,3×p...t×pp 不互质,显然, t=pk1,所以与 p 不互质的数有 pk1 个,那么与 p 互质的数就有 pkpk1 个,即 φ(pk)=pkpk1

计算欧拉函数

单个欧拉函数:设 n 的唯一分解式(就是质因数分解)为 n=p1k1×p2k2×...×psks显然, p1ps 均为质数,且 p1k1,p2k2...psks 两两互质),则 φ(n)=i=1sφ(piki)i=1sφ(piki) 能用性质三求解(其实就是 φ(n)=i=1s(pikipiki1))。

在代码实现中有一个更简单的式子:

φ(n)=n×i=1s(11pi)

代码:

点击查看代码
int phi(int x){ int num=x; for(int i=2;i*i<=x;i++){ if(x%i==0) num=num/i*(i-1); while(x%i==0) x/=i; } if(x>1) num=num/x*(x-1); return num; }

线性筛欧拉函数

在线性筛素数的同时可以筛出欧拉函数,设 pi 的最小质因子,分三种情况讨论:

  1. i 为质数,φ(i)=i1

  2. pip 的质因子,φ(i)=φ(ip)×p

  3. pip 互质,φ(i)=φ(ip)×φ(p)

证明:

  1. 显然

  2. i=pk×q,其中 q 不含质因子 p。则 φ(i)=φ(q)×φ(pk)=φ(q)×(pkpk1)=φ(q)×p(pk1pk2)=φ(q)×p×φ(pk1)=φ(ip)×p

  3. 根据积性可得。

例题:P2158 [SDOI2008] 仪仗队

假如 C君 在仪仗队的左前方,坐标为 (0,0)

首先可以发现,对于一个坐标 (i,j)C君 能看到这个位置当且仅当 gcd(i,j)=1,并且在与 C君 位置相交的对角线左右两边能看到的位置对称。

ans=2i=1n1j=1i[gcd(i,j)=1]+1

ans=2i=1n1φ(i)+1

用线性筛欧拉函数即可,记得当 n=1 时特判一下。

代码:

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define MAXN 40010 using namespace std; int n; int phi[MAXN]; bool vis[MAXN]; vector<int> prm; long long ans; void gg(){ phi[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) phi[i]=i-1,prm.push_back(i); for(auto j:prm){ if(i*j>n) break; vis[i*j]=true; if(i%j==0){ phi[i*j]=phi[i]*j;break; } phi[i*j]=phi[i]*phi[j]; } } } int main(){ cin>>n; if(n==1){ cout<<"0"; return 0; } gg(); for(int i=1;i<n;i++) ans+=phi[i]; cout<<ans*2+1; return 0; }

2023.10.9

发现一个可能有用的等式:

i=1ni[gcd(n,i)=1]=φ(n)n2

欧拉定理

对任意正整数 a,mgcd(a,m)=1,一定有:aφ(m)1(modm)

逆元

逆元补充 (by 2023.5.11)

定义

对于任意正整数 x 满足 x×x11(modp),则称 x1x 在模 p 同余下的逆元(其实就跟普通的 x×x1=1 中的 x1 差不多,只不过这里的逆元是在模 p 同余下,而且在模 p 同余下x×x1=1,然后就可以得出 xk×x1xk1(modp)

为了方便理解,我就先把下文中所有的字母下面带 0 的都为这个字母代表的数在模某一个数同余下的逆元。(因为我在听课时老师是用 x1 表示 x 在模 p 同余下的逆元QWQ)

性质(这些记住就行QWQ)

逆元存在性定理:x 在模 p 同余下存在逆元当且仅当 gcd(x,p)=1

推论:当且仅当模数 p 是质数时,[1,p1] 内所有整数都存在模 p 下的逆元。 0 没有逆元。

逆元唯一性定理:模 p 同余下,一个整数 x 的逆元若存在,则唯一。

定理:在模质数 p 同余下, [1,p1] 内所有整数的逆元互不相同。

定理:一个数的逆元的逆元等于它自身。

计算逆元

先上例题。

P1082 [NOIP2012 提高组] 同余方程

根据逆元定义可得这是一道求逆元板子题(显然)。

因为题目说输入数据保证一定有解,根据逆元存在性定理可得 gcd(a,b)=1

开始求逆元喽。

a×x1(modb)

a×x=1+b×k

a×x+b×k=1

(由于 k 为任意整数,我们不知道它是正数还是负数,就把前面的正负号省略了)

然后可以发现,这不就是一个不定方程吗。(因为 a,b 为已知量,只需求 x

在前面我们知道 gcd(a,b)=1,所以可以用递归求解即可。

最后记得把 x 取模到 [1,b1] 的范围即可。QWQ

代码如下。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define int long long using namespace std; inline void gg(int &x,int &y,int a,int b){ if(a%b==0){ x=0,y=1; }else{ int xx,yy; gg(xx,yy,b,a%b); x=yy; y=xx-a/b*yy; } } signed main(){ int a,b,x,y; cin>>a>>b; gg(x,y,a,b); cout<<(x%b+b)%b; return 0; }

当然也可以像下面这样写。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define int long long using namespace std; inline void gg(int &x,int &y,int a,int b){ if(a%b==0){ x=0,y=1; return; } gg(y,x,b,a%b); y-=a/b*x; } signed main(){ int a,b,x,y; cin>>a>>b; gg(x,y,a,b); cout<<(x%b+b)%b; return 0; }

补充

写于 2023.5.25

由于扩展中国剩余定理中有一些比较难懂的东西,结果发现是前面学的有问题,所以在这里来个补充。

先看一道题。(这是我自己出的QWQ)

axb(modp)

按上面的思路可得 ax+pk=b

由裴蜀定理可得,如果 gcd(a,p)b 则无解。

gcd(a,p)=g

用 exgcd 可求 au+pv=g

abug+pbvg=gbg=b

x=bugk=bvg

这说明 x0bug(modp)axb(modp) 的解。

x1axb(modp) 的其他解。

ax1ax0(modp)

所以 pax1ax0

这蕴含 pga(x1x0)g

因为 pgag 没有公约数。

所以 pgx1x0

x1=x0+kpgkZ

而相差 p 的倍数的解被规定相同,所以恰好有 g 个不同的解。

即取 k=0,1,...,g1


其实学完扩展中国剩余定理再回来看可以感觉到其实 ax+by=c 也不一定只有一个解或无解。(只不过在 OI 中我们只需要求差不多最小正整数解即可QWQ)

好像很显然的样子QWQ。

欧拉定理的继续

欧拉定理有一个前提条件就是 gcd(a,m)=1,根据逆元存在性定理就可得出这个 a 在模 m 同余下一定有逆元。让我们推个式子。QWQ

aφ(m)1(modm)

aφ(m)×a1a1(modm)

aφ(m)1a1(modm)

然后我们就得到了另一个求逆元的方法。(虽然不是很好用)

但是当 m 是质数时,aφ(m)1(modm) 就可以转化成 am11(modm) 于是费马小定理出现了

费马小定理

对任意整数 a 和质数 pap11(modp)

根据式子可以推出:

a1ap2(modp)

然后我们就又双叒叕得到了一个求逆元的方法(用快速幂求一下 ap2modp 即可)。

上个题QWQ P2613 【模板】有理数取余

abmod19260817 的值。

让我们算一算吧。

abmod19260817

a×b1mod19260817

所以我们只需算出 b19260817 同余下的逆元。由于 19260817 为一个质数,用费马小定理求即可。

而无解的情况就是 b19260817 倍数,那么 bmod19260817 一定等于 0

直接上代码。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define mod 19260817 using namespace std; inline long long read(){ char ch=getchar(),h;long long w=0; while(ch>'9' or ch<'0') h=ch,ch=getchar(); while(ch>='0' and ch<='9') w=(w*10+(ch-'0'))%mod,ch=getchar(); if(h=='-') w=-w; return w; } long long a,b; inline long long gg(long long x,long long k){ //求 x 在模 mod 同余下的逆元,即求 x^(mod-2)%mod 的值 //这里 k 就表示 mod-2 的值 long long ans=1; while(k){ if(k&1){ ans*=x;ans%=mod; } x*=x;x%=mod; k>>=1; } return ans; }//快速幂是啥偶就不讲了QWQ int main(){ a=read(),b=read(); if(b==0) cout<<"Angry!\n"; else cout<<a*gg(b,mod-2)%mod; return 0; }

线性求逆元

invi 表示 i 的逆元,有递推式:

invipi×invpmodi(modp)

边界为 inv1=1

又要开始推式子了。

p=i×k+r(r[1,n))

0i×k+r(modp)

ri×k(modp)

r×r1×i1i×i1×r1×k(modp)

i1k×r1(modp)

invipi×r1(modp)

invipi×invpmodi(modp)

再解释一下,我们刚开始设 p=i×k+r,其中 r[1,n)显然,k=pir=pmodi,这样应该能看懂了吧。QWQ

P3811 【模板】乘法逆元

下面是代码。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define MAXN 3000010 using namespace std; int n,p,inv[MAXN]; int main(){ ios::sync_with_stdio(false); cin>>n>>p; inv[1]=1; for(int i=2;i<=n;i++){ // inv[i]=(1ll*-(p/i)*inv[p%i]%p+p)%p; inv[i]=1ll*(p-p/i)*inv[p%i]%p;//这两个算式等价,只不过这个少取模一次 //实测,在P3811中第一个用398ms,第二个用338ms } for(int i=1;i<=n;i++) cout<<inv[i]<<'\n'; return 0; }

说句闲话,我刚开始做这道题时一直 TLE 了两个点,最后把换行符 endl 改为 '\n'AC 了,这个故事告诉我们,珍爱生命,远离 endl

B3717 组合数问题

首先知道:

Cnm=n!m!(nm)!

显然:

Cnmn!m!(nm)!(mod998244353)

之后就把 998244353 当做 p 就行了不然打一个九位数太麻烦了

n!m!(nm)!modp=n!×(m!)1×[(nm)!]1modp

然后求 (m!)1显然:

(m!)111×21×31×...×(m1)1×m1(modp)

所以先线性求一遍逆元,然后预处理出所有阶乘的逆元即可。

上代码。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 using namespace std; int T,N; const int p=998244353; int ans; int main(){ ios::sync_with_stdio(false); cin>>T>>N; vector<int> inv(N+1),a(N+1),b(N+1); inv[1]=1;//inv_i 表示 i 的逆元 a[1]=1;//a_i 表示 i!的值 b[0]=b[1]=1;//b_i 表示 i! 的逆元的值 for(int i=2;i<=N;i++){ inv[i]=1ll*(p-p/i)*inv[p%i]%p; a[i]=1ll*a[i-1]*i%p; b[i]=1ll*b[i-1]*inv[i]%p; } while(T--){ int n,m,num=1; cin>>n>>m; num=1ll*a[n]*b[m]%p*b[n-m]%p; ans^=num; } cout<<ans; return 0; }

威尔逊定理

(on 2023.7.11)

定理

p 为素数,那么:

(p1)!1(modp)

证明

p=2 时,(p1)!11(modp)

取一个大于 2 的质数 p,那么对于任意正整数 x
1xp1),它一定有一个逆元,且这 p1 个数的逆元不同。对于这些逆元,逆元是它本身的是 1p1。我们可以把剩下的整数 2,3,4,...,p1 分成 (p3)/2 份,每一份的乘积都模 p1,这说明:

2×3×4×...×(p2)1(modp)

再在两边同时乘上 1p1,得:

(p1)!p11(modp)

证毕。

它的逆定理为:

正整数 p 是质数的充要条件为:

(p1)!1(modp)

威尔逊定理用的很少,不过扩展卢卡斯定理可能要用。

中国剩余定理(CRT)

它是用来解决一个问题的。

问题

给定 n 个线性同余方程:

{xa1(modm1)xa2(modm2)...xan(modmn)

其中保证 m1,m2,...,mn 两两互质。求 x 的通解。

定理

上面的方程组有解,且按如下方式构造:

M=i=1nmiMi=MmitiMi 在模 mi 同余下的逆元。
则方程组的唯一通解是:

xi=1naitiMi(modM)

证明

显然:

gcd(Mi,mi)=1

且,

Miti1(modmi)

(能够保证 Mi 在模 mi 同余下一定有逆元)

aiMitiai(modmi)

另外,对于一个 jji),由于 mi|Mj,于是 Mj0(modmi)

aiMiti+mjMjtjai(modmi)

aiMiti+j=1,ijnajMjtjai(modmi)

i=1naiMitiai(modmi)

这就验证了 x=i=1naiMiti 是方程组的一组特解。

运用小学知识(其实就是我懒得证明)可得每两组相邻特解之间相差为 M 的倍数,于是 x+kM 也是一个特解,由此,通解唯一性得证。

例题

P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

模板题,直接上代码。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define MAXN 20 #define int long long using namespace std; int n; void gg(int &x,int &y,int a,int b){//求逆元 if(a%b==0){ x=0,y=1; return; } gg(y,x,b,a%b); y-=a/b*x; } int a[MAXN],b[MAXN],M[MAXN],m=1,t[MAXN],ans; signed main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]>>b[i];//x=ai*k+bi -> x mod ai=bi mod ai m*=a[i]; } for(int i=1;i<=n;i++){ M[i]=m/a[i]; int x=0,y=0; gg(x,y,M[i],a[i]); t[i]=(x%a[i]+a[i])%a[i]; } for(int i=1;i<=n;i++){ ans+=b[i]*t[i]%m*M[i]%m; ans%=m; } cout<<ans; return 0; }

P3868 [TJOI2009] 猜数字

b|(nai)

nai0(modbi)

nai(modbi)

然后套模板即可。

拓展中国剩余定理

等我学完再写。QWQ

终于学完力。QWQ(on 2023.5.23)

{xa1(modb1)xa2(modb2)...xan(modbn)

x 的通解,但是不保证 b1,b2,...,bn 两两互质

首先拿出前两个方程。

{xa1(modb1)xa2(modb2)

可变为

{x=b1k1+a1x=b2k2+a2

k1,k2 为任意整数)

所以

b1k1+a1=b2k2+a2

可变为

b1k1+b2k2=a2a1

(这里 k2 不是 k2 的解释和在用 exgcd 求逆元中 k 的解释相同)

g=gcd(b1,b2)

就可以用 exgcd 求出 b1h1+b2h2=g 的解。

h1,h2 就是 ax+by=gcd(a,b) 中的 x,y话说这个不应该看不懂吧QWQ

当然如果题目中不保证一定有解到话判断 (a2a1) 是否能被 g 整除即可。如果 g(a2a1) 则无解。

求出 h1h2 后可求出 k1=a2a1gh1

从而得到一组通解。

k1=k1+b2gt

k2=k2b1gt

t 为任意整数)

k1 带入 x=b1k1+a1 中。

x=b1k1+b1b2gt+a1

所以

xb1k1+a1(modlcm(b1,b2))

之后继续合并即可。

例题:P4777 【模板】扩展中国剩余定理(EXCRT)

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define MAXN 100010 #define ll long long #define int __int128 using namespace std; int n,a[MAXN],b[MAXN]; int gcd(int x,int y){ return y==0?x:gcd(y,x%y); } int lcm(int x,int y){ return x/gcd(x,y)*y; } void exgcd(ll a,ll b,int &x,int &y){ if(a%b==0){ x=0,y=1; return; } exgcd(b,a%b,y,x); y-=a/b*x; } ll cab_a,cab_b; int k1,k2,g,nem1,nem2,mod; void solve(int a,int b){ g=gcd(cab_b,b); if((a-cab_a)%g!=0) exit(0);//无解 exgcd(cab_b,b,nem1,nem2); k1=(a-cab_a)/g*nem1; k1=(k1%(b/g)+(b/g))%(b/g);//将 k1 变成正整数 mod=lcm(cab_b,b); cab_a=((cab_b*k1+cab_a)%mod+mod)%mod;//将 cab_a 变成正整数 cab_b=mod; } signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>b[i]>>a[i]; cab_a=a[1],cab_b=b[1]; for(int i=2;i<=n;i++){ solve(a[i],b[i]); } cout<<cab_a%cab_b; return 0; }

再来解释一下,其实 ax+by=gcd(a,b) 如果有解则解有无数个,它的通解为:

{x=x+kbgcd(a,b)y=ykagcd(a,b)

k 为任意整数)

把新的 x,y 带入 ax+by=gcd(a,b) 是可以发现方程还是成立的。

由于题目中要求的是最小非负整数解,所以在代码中要将变量变为正整数。

容斥原理

关于集合的概念看 这里

说句闲话,我开始了解集合也是开始于上面的链接。

我们有 A,B,C 三个集合,求 |ABC| ,可以列出式子:

|ABC|=|A|+|B|+|C||AB||AC||BC|+|ABC|

如果我们有 n 个集合 P1,P2,...,Pn,则:

|i=1nPi|=S[1,2,...,n](1)|S|1|sSPs|

二项式定理

(写于 2023.4.29)

这个主要说的就是 (nm) 这东西。

二项式定理

Lucas定理

定义

对于一个质数 p

(nm)(npmp)(nmodpmmodp)(modp)

证明我暂时不会,先放个链接QWQ。

链接

但是现在会了。QWQ(by 2023.7.11)

证明

在证明之前,要证明一个式子。

(1+x)p1+xp(modp)

(其中 p 为质数)

证明这个式子

首先知道 (1+x)p=k=0p(pk)xk

对于 k=0p(pk),由于知道 p 为质数,所以这里的 (pk) 一定是 p 的倍数,即 (pk)0(modp),因为 (pk)=p!k!(pk)!,而想要在 p!k!(pk)! 消掉 p 只能是 k=ppk=p,即 k=p0 ,而对于其他的,分子中有 p 这个因数,并且 p 是质数,所以只有分母有 p 这个因数才行。

所以我们可以得出:

(1+x)p1+k=1p1(pk)xk+xp1+xpmodp

证毕。


现在开始正式证明卢卡斯定理。

先设 n=knp+rnm=kmp+rm,所以 np=knmp=kmnmodp=rnmmodp=rm

(1+x)n=(1+x)knp+rn=(1+x)knp×(1+x)rn=((1+x)p)kn×(1+x)rn(1+xp)kn×(1+x)rn(modp)i=0kn(kni)xip×i=0rn(rni)xi(modp)

那就有:

i=0n(ni)xii=0kn(kni)xip×i=0rn(rni)xi(modp)

对于任意一个整数 z,必定有一组 i,j 满足 xz=xipxj。所以 z=ip+j,那就是说上面的式子中 i 取任意一个,右面都有一个新的跟它恒等(应该是在模 p 意义下)。

当上面的式子左面的 i=m 时,式子右面则分别 i=km=mpi=rm=mmodp

则:

(nm)xm(knkm)xkmp×(rnrm)xrm(modp)(knkm)(rnrm)xkmpxrm(modp)(knkm)(rnrm)xkmp+rm(modp)(knkm)(rnrm)xm(modp)

两边同乘 xm 的逆元,便可得出:

(nm)(knkm)(rnrm)(modp)

即:

(nm)(npmp)(nmodpmmodp)(modp)

证毕。


p 不为质数时,就需要用到 exLucas 定理了。

exLucas 定理之后再学。QWQ

例题

P3807 【模板】卢卡斯定理/Lucas 定理

模板题。听君一席话,如听一席话。

直接上代码。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 using namespace std; int t; int qpow(int x,int y,int p){//快速幂 int ans=1; while(y){ if(y&1) ans=1ll*ans*x%p; x=1ll*x*x%p; y>>=1; } return ans; } int C(int n,int m,int p){//组合数 if(n<m) return 0; int a=1,b=1; for(int i=1;i<=n;i++) a=1ll*a*i%p; for(int i=1;i<=m;i++) b=1ll*b*i%p; for(int i=1;i<=n-m;i++) b=1ll*b*i%p; return 1ll*a*qpow(b,p-2,p)%p; } int Lucas(int n,int m,int p){//Lucas 递推 if(n<p and m<p) return C(n,m,p); return 1ll*Lucas(n/p,m/p,p)*C(n%p,m%p,p)%p; } int main(){ cin>>t; while(t--){ int n,m,p; cin>>n>>m>>p; cout<<Lucas(n+m,m,p)<<'\n'; } return 0; }

莫比乌斯反演

莫比乌斯反演

其他

这里是其他的一些数论题的题解(算是吧)。

(写于 2023.5.23)

P3708 koishi的数学题

很有意思的一道题。

首先我看到这道题后思考如何由 f(x) 转变到 f(x+1)。(时间复杂度最好为 O(1)

f(x)=i=1nxmodi

f(x)=i=1nxxii

f(x)=nxi=1nxii

那么:

f(x+1)=n(x+1)i=1nx+1ii

f(x+1)=n+nxi=1nx+1ii

思考从 ni=1xiini=1x+1ii 数值的变化。

可以发现,只有 i|(x+1),从 xix+1i 的值会加 1,那么总共的值会加 i

所以总共增加的值为 x+1 的因数之和。

所以 f(x+1)=f(x)+nax+1ax+1x+1 的因数之和)

而且 f(1)=n1。(显然

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define MAXN 1000010 using namespace std; int n,a[MAXN]; long long ans; void cab(){//预处理 for(int i=1;i<=n;i++){ for(int j=i;j<=n;j+=i){ a[j]+=i; } } } void solve(){//解决问题 ans=n-1; cout<<ans<<" "; for(int i=2;i<=n;i++){ ans=ans+n-a[i]; cout<<ans<<" "; } } int main(){ cin>>n; cab(); solve(); return 0; }

on 2023.8.12。

P2568 GCD

就是求 pprimei=1nj=1n[gcd(i,j)==p]。由 gcd(i,j)=p 可得 gcd(ip,jp)=1,所以说原式可以变成 pprimei=1npj=1np[gcd(i,j)==1],不难发现 i=1npj=1np[gcd(i,j)==1]P2158 [SDOI2008] 仪仗队 中的式子很像,所以直接线性求欧拉函数即可。

点击查看代码
#include<bits/stdc++.h> #define XD 114514 #define MAXN 10000010 #define int long long using namespace std; int n; int sum[MAXN],phi[MAXN],tot,prime[MAXN]; bool vis[MAXN]; long long ans; void solve(){ phi[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) prime[++tot]=i,phi[i]=i-1; for(int j=1;j<=tot and i*prime[j]<=n;j++){ vis[i*prime[j]]=true; if(i%prime[j]==0){ phi[i*prime[j]]=phi[i]*prime[j]; break; }else{ phi[i*prime[j]]=phi[i]*phi[prime[j]]; } } } for(int i=1;i<=n;i++) sum[i]=sum[i-1]+phi[i]; } signed main(){ cin>>n; solve(); for(int i=1;i<=tot;i++) ans+=2*sum[n/prime[i]]-1; cout<<ans; return 0; }

__EOF__

本文作者wdgm4
本文链接https://www.cnblogs.com/wdgm4/p/17586239.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   wdgm4  阅读(147)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示