2.14數論

類歐幾里得算法

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
擴展歐拉定理(對於a,p不互質的情況計算a%p):https://blog.csdn.net/hzj1054689699/article/details/80693756

二次剩餘

https://blog.csdn.net/kele52he/article/details/78897187
對於n整數a若存在整數x 滿足 x^2%n=a那麼稱a是mod n 的二次剩餘
判定:當且僅當 a^(p-1)/2=1
引理:對於奇素數p下的二次剩餘n,有兩個不同的x滿足x^2%p=n
一個奇素數的完全剩餘系下有(p-1)/2 個二次剩餘。

Cipolla 算法

求解 x^2=n mod p,O(nlogn)O(n\log n)

步驟:
1.隨機一個 a,使得 (a2n)(p1)/2=1mod  p(a^2-n)^{(p-1)/2}=-1\mod p,期望次數 2 次。
2.令 ω=a2n\omega=\sqrt{a^2-n},擴域爲虛根。
3.(a+ω)(p+1)/2(a+\omega)^{(p+1)/2} 即爲所求。

//xy%p=xy-(xy)/p*p
inline ll qmul(ll a,ll b,ll mod)
{
    a%=mod,b%=mod;
    return (((a*b)-(ll)((ll)((long double)a/mod*b+1e-3)*mod))%mod+mod)%mod;
}

擴展BSGS

在這裏插入圖片描述

狄利克雷卷積的逆

ff1=ef*f^{-1}=e。通過構造,可以 O(nlogn)O(n\log n) 構造出函數的逆。
如果積性函數 ffgg 對於任意質數 pp 滿足 f(p)=g(p)=1f(p)=g(p)=1,我們就可以快速求出 ff 的前綴和。
h=fg1h=f*g^{-1},那麼就有 f(p)=g(1)h(p)+g(p)h(1)=h(p)+1=1f(p)=g(1)h(p)+g(p)h(1)=h(p)+1=1,推出 h(p)=0h(p)=0
我們要求的是:
i=1nf(i)=i=1ndig(d)h(id)=d=1nh(d)i=1n/dg(i) \sum_{i=1}^n f(i) = \sum_{i=1}^n\sum_{d|i}g(d)h(\frac i d) = \sum_{d=1}^nh(d)\sum_{i=1}^{n/d} g(i)
由於 hh 只在所有質因子次數都大於 1 的情況下才不爲零,這樣的數可以寫成 a2b3a^2b^3 的形式,不超過 O(n)O(\sqrt n) 個。枚舉 aa,枚舉 bb,就得到了 a2b3a^2b^3 的質因數分解,即可快速計算。

狄利克雷生成函數

F(s)=i=0f(i)/isF(s)=\sum_{i=0}^{\infin}f(i)/i^s
多項式卷積即爲狄利克雷卷積。

定義求導算子 ':當 pp 爲質數時,p=1p'=1,對於整數 aabb,有 (ab)=ab+ab(ab)'=a'b+ab'

1

i=1ngcd(i,i)\sum_{i=1}^n \gcd(i,i')
??? ??? ???

min_25 篩

作用:計算積性函數前綴和。複雜度 O(n3/4logn)O(\frac{n^{3/4}}{\log n})
要求:f(p)f(p) 爲關於 pp 的低次多項式,f(pc)f(p^c) 能很快計算。

第一步

我們想要求所有質數的函數值。考慮用質數把合數的貢獻篩掉。記 g(n,j)=i=1n[mind(i)>pj] ikg(n,j)=\sum_{i=1}^n [mind(i)>p_j]~i^kmind(i)mind(i) 表示 ii 的最小質因子。
初始,我們知道 g(n,0)g(n,0) 的值,也就是一個自然數冪和(這就是要求 f(p)f(p) 是一個低次多項式的原因)。因此我們按照 jj 從小到大的順序轉移 gg。考慮 g(n,j1)g(n,j-1) 轉移到 g(n,j)g(n,j),有哪些數變得不合法了。就是那些最小質因子剛好爲 pjp_j 的那些數。
注意到,如果 pj2p_j^2 如果大於 xx,由於沒有小於 xx 的合數含有 pjp_j,因此直接轉移過來。
g(n,j)={g(n,j1)pjk(g(n/pj,j1)g(pj1,j1))pj2ng(n,j1)pj2>ng(n,j)= \begin{cases} g(n,j-1)-p_j^k\big(g(n/p_j,j-1)-g(p_j-1,j-1)\big)&,p_j^2\leq n\\ g(n,j-1)&,p_j^2>n \end{cases}
第一維只有根號 nn 大小。發現求 gg 的過程與積性函數沒關係,因此可以求 11nn 質數個數。

第二步

考慮用這個東西求函數的前綴和。定義 s(n,j)=i=1n[mind(i)pj] f(i)s(n,j)=\sum_{i=1}^n[mind(i)\geq p_j]~f(i)。目標是求 s(n,1)s(n,1),邊界條件是 s(n,p)s(n,|p|) 爲所有質數的答案。因此我們遞歸計算 ss。轉移的時候枚舉最小質因子,枚舉最小質因子 pkp_k 的次數 ees[n/pke][k+1]s[n/p_k^e][k+1]。注意單獨考慮 f(pke)f(p_k^e)

s(n,j)=g(n,p)i=1j1f(pi)+kje(f(pke)s(n/pke,k+1)+f(pke+1)) s(n,j)=g(n,|p|)-\sum_{i=1}^{j-1}f(p_i)+\sum_{k\geq j}\sum_e\big(f(p_k^e)s(n/p_k^e,k+1)+f(p_k^{e+1}) \big)

Loj6053 簡單的函數
Notice: 1 拿出來最後單獨統計,val 數組裏的值從大到小,數組開到兩倍根號。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define ld long double
using namespace std;
const int mod=1e9+7,M=200010,inv2=(mod+1)/2;
int ind1[M],ind2[M];
bool np[M];
ll sum[M],p[M],cnt,tot,h[M],g[M],val[M],n,B;
int read()
{
	int x=0;char c=getchar(),flag='+';
	while(!isdigit(c)) flag=c,c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return flag=='-'?-x:x;
}
void init(int n)
{
	np[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!np[i]) p[++cnt]=i;
		for(int j=1;j<=cnt&&i*p[j]<=n;j++) 
		{
			np[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
	for(int i=1;i<=cnt;i++) sum[i]=(sum[i-1]+p[i])%mod;
}
ll S(ll x,int j)
{
	if(x<=1||x<p[j]) return 0;
	int t=(x<=B)?ind1[x]:ind2[n/x];
	ll ans=g[t]-sum[j-1]-h[t]+(j-1);
	if(j==1) ans+=2;
	for(int k=j;k<=cnt&&p[k]*p[k]<=x;k++)
	{
		ll t1=p[k],t2=p[k]*p[k];
		for(int e=1;t2<=x;e++,t1=t2,t2*=p[k])
			ans=(ans+(p[k]^e)*S(x/t1,k+1)+(p[k]^(e+1)))%mod;
	}
	return ans;
}
int main()
{
	cin>>n;
	init(B=sqrt(n)+1);
	for(ll l=1,r;l<=n;l=r+1)
	{
		r=n/(n/l);
		val[++tot]=n/l;
		if(n/l<=B) ind1[n/l]=tot;
		else ind2[r]=tot;
		ll t=(n/l)%mod;
		h[tot]=(t-1)%mod;
		g[tot]=(2+t)*(t-1)%mod*inv2%mod;
	}
	for(int j=1;j<=cnt;j++)
	{
		for(int i=1;i<=tot&&p[j]*p[j]<=val[i];i++)
		{
			int t=(val[i]/p[j]<=B)?ind1[val[i]/p[j]]:ind2[n/(val[i]/p[j])];
			g[i]=(g[i]-p[j]*(g[t]-sum[j-1]))%mod;
			h[i]=(h[i]-h[t]+j-1)%mod; 
		}
	}
	cout<<(S(n,1)%mod+1+mod)%mod;
	return 0;
}
/*by DT_Kang*/
發佈了89 篇原創文章 · 獲贊 26 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章