【leecode 劍指offer】 從上到下列印二叉樹 II
Warning
由於式子較多,LaTeX 渲染可能會有點慢qwq
上一次學莫反失敗了……因為看了太多部落格而又沒有弄懂,每個部落格裡面寫的都不一樣,最後徹底看懵……
現在看之前的樣子感覺好zz啊
0. 前置-整除分塊
問題
給定一個 \(n\) ,求:
\[\sum_{i=1}^n\Big\lfloor \frac{n}{i}\Big\rfloor \]思路&程式碼
這個式子顯然可以 \(\mathcal{O}(n)\) 計算,考慮優化。
對於小的數可能感覺不出來;但是對於較大的數,比如:\(100/51,100/52,\dots,100/100\) ,你會發現它們全都是一樣的!這說明其實剛才的過程中,有很大一部分的計算完全可以一次性解決!
現在來考慮如何計算左右端點。設當前左右端點為 \(l,r\) ,那麼顯然有:
\[n/l=k,n=r\times k,=>r=n/(n/l). \]於是就可以直接得到右端點了。容易得到這樣的複雜度是 \(\mathcal{O}(\sqrt n)\) .
Code
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
習題
給定 \(n\) 個數 \(a_1\sim a_n\) ,求最大的 \(d\) ,滿足
\[\sum_{i=1}^n d-((a_i-1)\bmod d+1)\leq k. \]展開取模,得到
\[\begin{aligned} \sum_{i=1}^nd-((a_i-1)-d\times \Big\lfloor\frac{a_i-1}{d}\Big\rfloor+1)\leq k &\iff \sum d\times\Big\lfloor\frac{a_i-1}{d}\Big\rfloor-(a_i-1)-1\leq k-n\times d\\\\ & \iff \sum d\times\Big\lfloor \frac{a_i-1}{d}\Big\rfloor \leq k-n\times d+\sum a_i\\\\ & \iff \sum \Big\lfloor \frac{a_i-1}{d}\Big\rfloor\leq \Big\lfloor\dfrac{k-n\times d+\sum a_i}{d}\Big\rfloor\\\\ & \iff \sum \Big\lfloor \frac{a_i-1}{d}\Big\rfloor\leq \Big\lfloor\dfrac{k+\sum a_i}{d}\Big\rfloor-n\\\\ \end{aligned} \]於是發現了一個整除分塊的基本式,可以直接求了。
Code
//Author:RingweEH
const int N=110;
int n;
ll a[N],k,ans=0;
int main()
{
n=read(); k=read(); ll mx=0;
for ( int i=1; i<=n; i++ )
a[i]=read()-1,k+=a[i]+1,mx=max( mx,a[i] );
for ( ll l=1,r=1; r<=mx; l=r+1 )
{
ll sum=0; r=(1ll<<50);
for ( int i=1; i<=n; i++ )
if ( a[i]>=l ) r=min( r,a[i]/(a[i]/l) ),sum+=a[i]/l;
ll tmp=k/(sum+n);
if ( l<=tmp ) ans=max( ans,min( tmp,r ) );
}
if ( k/n>mx ) ans=max( ans,k/n );
printf( "%lld\n",ans );
return 0;
}
0. 前置-積性函式
定義 :如果 \(\gcd(x,y)=1\) 且 \(f(xy)=f(x)f(y)\) ,那麼 \(f(n)\) 為積性函式。
性質:如果 \(f(x),g(x)\) 均為積性函式,那麼如下函式也是積性函式:
\[h(x)=f(x^p)\\ h(x)=f^p(x)\\ h(x)=f(x)g(x)\\ h(x)=\sum_{d|x} f(d)g(\frac{x}{d}) \]常見的積性函式 :
- 單位函式。\(\varepsilon(x)=[x=1]\)
- 常數函式。\(1(x)=1\)
- 尤拉函式。\(\varphi(x)=\sum_{i=1}^x[\gcd(x,i)=1]\)
- 莫比烏斯函式
0. 前置-Dirichlet卷積
定義 :兩個 數論函式 \(f,g\) 的Dirichlet卷積 \((f*g)\) 為:
\[(f*g)(x)=\sum_{d|x}f(d)g(\frac{x}{d}) \](會發現這個式子和上面積性函式的最後一個式子很像……)
性質 :滿足交換律和結合律,\(\varepsilon\) 是該函式單位元。
舉例 :
\[d(x)=(1*1)(x)=\sum_{d|1}1\\ \sigma(x)=(1*d)(x)=\sum\limits_{d|x}d\\ \varepsilon(x)=(\mu*1)(x)=\sum\limits_{d|x}\mu(d)\\ \varphi(x)=(\mu*ID)(x)=\sum\limits_{d|x}d\cdot\mu(\frac xd) \]1. 莫比烏斯函式
定義 :
\[\mu(x)= \begin{cases} 1 & x=1\\ 0 & \exists d\in\mathbb{Z}:d^2\mid x\\ (-1)^k & k 為 x 本質不同的的質因子個數\\ \end{cases} \]另一種表述 :
用算數基本定理把 \(x\) 寫成質因數分解的形式得到 \(x=\prod_{i=1}^k p_i^{c_i}\) ,那麼有:
- \(x=1\) ,\(\mu(x)=1\)
- \(\forall i,c_i=1\) ,\(\mu(x)=(-1)^k\)
- \(otherwise,\mu(x)=0\)
性質 :
-
\(\mu*1=\varepsilon\) ,即 \(\sum_{d|x}\mu(d)=\varepsilon(x)\) ,也就是 \(\sum_{d|x}\mu(d)=1\) 當且僅當 \(x=1\) ,否則 \(\sum_{d|x} \mu(d)=0\) .
- \[\sum_{d|n}\frac{\mu(d)}{d}=\frac{\varphi(n)}{n} \]
-
\[[\gcd(i,j)=1]=\varepsilon(\gcd(i,j))=\sum_{d|\gcd(i,j)}\mu(d)
\]
反演結論。
-
\[\varphi*1=ID,\sum_{d|nm}=\sum_{x|n}\sum_{y|m}[\gcd(x,y)=1]
\]
拓展。
線性篩莫比烏斯函式
int mu[N],pri[N],tot,is[N];
void sieve()
{
is[1]=mu[1]=1;
for ( int i=2; i<=n; i++ )
{
if ( !is[i] ) pri[++tot]=i,mu[i]=-1; //根據定義,質數的莫比烏斯函式顯然是-1
for ( int j=1; j<=tot && i*pri[j]<=n; j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j] ) mu[i*pri[j]]=-mu[i]; //多了一個質因子,取反
else { mu[i*pri[j]]=0;break; } //出現平方因子,置為0
}
}
}
2. 莫比烏斯反演
對於兩個數論函式 \(f,g\) ,滿足
\[f(x)=\sum_{d|x}g(d) \]那麼有
\[g(x)=\sum_{d|x}f(d)\times \mu(\frac{x}{d}). \]另一種表述方式(根據Dirichlet卷積):
\[f=g*1=>g=f*\mu \]Proof
\[\begin{aligned} f*\mu & =g*1*\mu\\ & =g*\varepsilon(莫比烏斯函式性質1)\\ & =g(單位函式) \end{aligned} \]
3. 例題
注:以下預設 \(n\leq m\) .
Problem b
求
\[\sum_{x=a}^b\sum_{y=c}^d[\gcd(x,y)=k] \]Solution
原式有上下界限制,不好求,一個簡單的想法是考慮容斥。
\[\begin{aligned} \sum_{x=a}^b\sum_{y=c}^d[\gcd(x,y)=k]&=\sum_{x=1}^b\sum_{y=1}^d[\gcd(x,y)=k]-\sum_{x=1}^{a-1}\sum_{y=1}^d[\gcd(x,y)=k]-\sum_{x=1}^{b}\sum_{y=1}^{c-1}[\gcd(x,y)=k]+\sum_{x=1}^{a-1}\sum_{y=1}^{c-1}[\gcd(x,y)=k]\\ \end{aligned} \]問題就轉化為求
\[\begin{aligned} f(n,m)= \sum_{x=1}^n\sum_{y=1}^m[\gcd(x,y)=k] &=\sum_{x=1}^{\lfloor n/k\rfloor}\sum_{y=1}^{\lfloor m/k\rfloor}[\gcd(x,y)=1]\\ \because [\gcd(i,j)=1]=\sum_{d|\gcd(i,j)}\mu(d) \therefore & =\sum_{x=1}^{\lfloor n/k\rfloor}\sum_{y=1}^{\lfloor m/k\rfloor}\sum_{d|\gcd(x,y)}\mu(d)\\ &=\sum_{d=1}^{\lfloor n/k\rfloor} \mu(d)\sum_{x=1}^{\lfloor n/k\rfloor}\sum_{y=1}^{\lfloor m/k\rfloor}[d|\gcd(x,y)]\\ \because 1\sim n中d的倍數有 \lfloor n/d\rfloor 個\therefore &=\sum_{d=1}^{\lfloor n/k\rfloor} \mu(d)\Big\lfloor \frac{n}{kd}\Big\rfloor \Big\lfloor \frac{m}{kd}\Big\rfloor \end{aligned} \]對 \(\mu(x)\) 函式求字首和,然後整除分塊即可。
Code
//Author: RingweEH
const int N=5e4+10;
int n,m,k,mu[N],sum[N],pri[N],tot=0;
bool is[N];
void init()
{
is[1]=mu[1]=1;
for ( int i=2; i<=N-10; i++ )
{
if ( !is[i] ) pri[++tot]=i,mu[i]=-1;
for ( int j=1; j<=tot && (i*pri[j])<=(N-10); j++ )
{
if ( i%pri[j] ) is[i*pri[j]]=1,mu[i*pri[j]]=-mu[i];
else { mu[i*pri[j]]=0; is[i*pri[j]]=1; break; }
}
}
sum[0]=0;
for ( int i=1; i<=(N-10); i++ )
sum[i]=sum[i-1]+mu[i];
}
int get_Fenk( int n,int m )
{
int res=0;
if ( n>m ) swap( n,m );
for ( int l=1,r=0; l<=n; l=r+1 )
{
r=min( n/(n/l),m/(m/l) );
res=res+(sum[r]-sum[l-1])*(n/l)*(m/l);
}
return res;
}
int main()
{
int T=read(); init();
while ( T-- )
{
int a=read(),b=read(),c=read(),d=read(),k=read();
a--; c--; a/=k,b/=k,c/=k,d/=k;
int ans=get_Fenk(b,d)-get_Fenk(a,d)-get_Fenk(b,c)+get_Fenk(a,c);
printf( "%d\n",ans );
}
return 0;
}
YY的GCD
給定 \(N,M(N,M\leq 1e7)\) ,求
\[\sum_{x=1}^N\sum_{y=1}^M[\gcd(x,y)\in primes] \]多測,\(T\leq 1e4\) .
Solution
\[\begin{aligned} \sum_{x=1}^N\sum_{y=1}^M[\gcd(x,y)\in primes] & =\sum_{p\in primes}\sum_{x=1}^N\sum_{y=1}^M[\gcd(x,y)=p]\\ & =\sum_{p\in primes}\sum_{x=1}^{\lfloor N/p\rfloor}\sum_{y=1}^{\lfloor M/p\rfloor}[\gcd(x,y)=1]\\ & =\sum_{p\in primes}\sum_{x=1}^{\lfloor N/p\rfloor}\sum_{y=1}^{\lfloor M/p\rfloor}\sum_{d|\gcd(x,y)}\mu(d)\\ &=\sum_{p\in primes}\sum_{d=1}^{\lfloor N/p\rfloor}\mu(d)\sum_{x=1}^{\lfloor N/p\rfloor}\sum_{y=1}^{\lfloor M/p\rfloor}[d|\gcd(x,y)]\\ &=\sum_{p\in primes}\sum_{d=1}^{\lfloor N/p\rfloor}\mu(d)\Big\lfloor \frac{N}{pd}\Big\rfloor \Big\lfloor \frac{M}{pd}\Big\rfloor\\ \end{aligned} \]令 \(t=pd\) ,有
\[\begin{aligned} \sum_{p\in primes}\sum_{d=1}^{\lfloor N/p\rfloor}\mu(d)\Big\lfloor \frac{N}{pd}\Big\rfloor \Big\lfloor \frac{M}{pd}\Big\rfloor &=\sum_{t=1}^N\sum_{p\in primes,p|t}\mu(\frac{t}{p})\Big\lfloor \frac{N}{t}\Big\rfloor \Big\lfloor \frac{M}{t}\Big\rfloor\\ \end{aligned} \]令
\[f(x)=\sum_{p\in primes,p|x}\mu(\frac{x}{p}) \]那麼所求就是
\[\sum_{x=1}^nf(x)\Big\lfloor \frac{N}{x}\Big\rfloor \Big\lfloor \frac{M}{x}\Big\rfloor \]把 \(f(x)\) 及其字首和預處理出來,然後整除分塊即可。
怎麼一模一樣啊 基本就是把上一題裡面的 \(k\) 換成不確定的,多了一個求和而已。
Code
//Author: RingweEH
const int N=1e7+10;
int n,m,mu[N],pri[N],tot=0,f[N];
ll sum[N];
bool is[N];
void init()
{
is[1]=mu[1]=1;
for ( int i=2; i<=N-10; i++ )
{
if ( !is[i] ) pri[++tot]=i,mu[i]=-1;
for ( int j=1; j<=tot && (i*pri[j])<=(N-10); j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j] ) mu[i*pri[j]]=-mu[i];
else { mu[i*pri[j]]=0; break; }
}
}
for ( int i=1; i<=tot; i++ )
for ( int j=pri[i]; j<=N-10; j+=pri[i] )
f[j]+=mu[j/pri[i]];
sum[0]=0;
for ( int i=1; i<=N-10; i++ )
sum[i]=sum[i-1]+f[i];
}
int main()
{
int T=read(); init();
while ( T-- )
{
n=read(); m=read();
if ( n>m ) swap( n,m );
ll ans=0;
for ( int l=1,r=0; l<=n; l=r+1 )
{
r=min( n/(n/l),m/(m/l) );
ll tmp=sum[r]-sum[l-1];
ans+=tmp*(n/l)*(m/l);
}
printf( "%lld\n",ans );
}
return 0;
}
LCM Sum
求
\[\sum_{i=1}^n \text{lcm}(i,n) \]Solution
顯然 \(\text{lcm}\) 可以轉化成 \(\gcd\) .
\[\begin{aligned} \sum_{i=1}^n\text{lcm}(i,n) &=\sum_{i=1}^n \dfrac{i\times n}{\gcd(i,n)}\\ &=\sum_{d|n}\sum_{i=1}^n[\gcd(i,n)=d]\frac{i}{d}\times n\\ &=\sum_{d|n}\sum_{i=1}^{n/d}[\gcd(i,n/d)=1]\times i\times n\\ &=n\times\sum_{d|n}\sum_{i=1}^{n/d}[\gcd(i,n/d)=1]i\\ \end{aligned} \]用 \(d\) 代換 \(n/d\) 得
\[\begin{aligned} & =n\times \sum_{d|n}\sum_{i=1}^d[\gcd(i,d)=1]\times i \end{aligned} \]感覺推不下去了……嗎?
會發現一個奇妙的東西:\(\sum_{i=1}^d[\gcd(i,d)=1]\times i\) 就是 \([1,d]\) 中與 \(d\) 互質的數的和!但還是不會求
注意到如果 \(\gcd(i,d)=1\) ,那麼有 \(\gcd(d-i,d)=1\) .也就是說,這樣的數成對出現,且每一對的和為 \(d\) .所以有
\[\sum_{i=1}^d[\gcd(i,d)=1]\times i=\frac{\varphi(d)\times d}{2} \]特殊情況:當 \(d=1\) 時,和為 \(1\) . 因此,
\[\begin{aligned} f(n)=n\times \sum_{d|n}\sum_{i=1}^d[\gcd(i,d)=1]\times i &=n\times \sum_{d|n}\frac{\varphi(d)\times d}{2} \end{aligned} \]Code
//Author: RingweEH
const int N=1e6+10;
int n,phi[N],pri[N],tot=0;
ll f[N];
bool is[N];
void init()
{
is[1]=phi[1]=1;
for ( int i=2; i<=N-10; i++ )
{
if ( !is[i] ) pri[++tot]=i,phi[i]=i-1;
for ( int j=1; j<=tot && (pri[j]*i)<=(N-10); j++ )
{
if ( i%pri[j] ) is[i*pri[j]]=1,phi[i*pri[j]]=phi[i]*phi[pri[j]];
else { is[i*pri[j]]=1; phi[i*pri[j]]=phi[i]*pri[j]; break; }
}
}
f[1]=1;
for ( int i=2; i<=N-10; i++ )
f[i]=1ll*phi[i]*i/2;
for ( int j=1; j<=tot; j++ )
for ( int i=1; i*pri[j]<=N-10; i++ )
f[i*pri[j]]+=f[i];
}
int main()
{
int T=read(); init();
while ( T-- )
{
int n=read();
ll ans=f[n]*n;
printf( "%lld\n",ans );
}
return 0;
}
Crash的數字表格 / JZPTAB
求
\[\sum_{i=1}^n\sum_{j=1}^m\text{lcm}(i,j)\bmod 20101009 \]Solution
\[\begin{split} \sum\limits_{i=1}^n\sum\limits_{j=1}^m\operatorname{lcm}(i,j)=&\sum\limits_{i=1}^n\sum\limits_{j=1}^m\frac{ij}{\gcd(i,j)}\\ =&\sum\limits_{d=1}^n\sum\limits_{i=1}^n\sum\limits_{j=1}^m\frac{ij}{d}[\gcd(i,j)=d]\\ =&\sum\limits_{d=1}^n\sum\limits_{i=1}^{\lfloor n/d\rfloor}\sum\limits_{j=1}^{\lfloor m/d\rfloor}ijd[\gcd(i,j)=1]\\ =&\sum\limits_{d=1}^n d\sum\limits_{i=1}^{\lfloor n/d\rfloor}i\sum\limits_{j=1}^{\lfloor m/d\rfloor}j\sum\limits_{k|\gcd(i,j)}\mu(k)\\ =&\sum\limits_{d=1}^n d\sum\limits_{k=1}^n\mu(k)\sum\limits_{i=1}^{\lfloor n/d\rfloor}i[k|i]\sum\limits_{j=1}^{\lfloor m/d\rfloor}j[k|j]\\ =&\sum\limits_{d=1}^n d\sum\limits_{k=1}^n\mu(k)\sum\limits_{i=1}^{\lfloor n/dk\rfloor}ik\sum\limits_{j=1}^{\lfloor m/dk\rfloor}jk\\ =&\sum\limits_{d=1}^n d\sum\limits_{k=1}^nk^2\mu(k)\frac{\lfloor n/dk\rfloor(\lfloor n/dk\rfloor+1)}{2}\cdot\frac{\lfloor m/dk\rfloor(\lfloor m/dk\rfloor+1)}{2}\\ \end{split} \]將 \(x=dk\) 帶入得
\[\sum\limits_{x=1}^nx\cdot\frac{\lfloor\frac{n}{x}\rfloor(\lfloor\frac{n}{x}\rfloor+1)}{2}\cdot\frac{\lfloor\frac{m}{x}\rfloor(\lfloor\frac{m}{x}\rfloor+1)}{2}\sum\limits_{k|x}k\mu(k) \]跟上題一樣求即可。
Code
//Author: RingweEH
const int N=1e7+10;
const ll Mod=20101009;
int n,m,mu[N],pri[N],tot=0;
ll f[N];
bool is[N];
void init()
{
is[1]=mu[1]=1;
for ( int i=2; i<=N-10; i++ )
{
if ( !is[i] ) pri[++tot]=i,mu[i]=-1;
for ( int j=1; j<=tot && (i*pri[j])<=(N-10); j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j] ) mu[i*pri[j]]=-mu[i];
else { mu[i*pri[j]]=0; break; }
}
}
f[1]=1;
for ( int i=2; i<=(N-10); i++ )
f[i]=(1ll*mu[i]*i+Mod)%Mod;
for ( int j=1; j<=tot; j++ )
for ( int i=1; i*pri[j]<=(N-10); i++ )
f[i*pri[j]]=(f[i*pri[j]]+f[i])%Mod;
}
ll func( int x )
{
ll res=1ll*x*f[x]%Mod;
(res*=1ll*(n/x+1)*(n/x)/2%Mod)%=Mod;
(res*=1ll*(m/x+1)*(m/x)/2%Mod)%=Mod;
return res;
}
int main()
{
init();
n=read(); m=read();
if ( n>m ) swap( n,m );
ll ans=0;
for ( int i=1; i<=n; i++ )
ans=(ans+func(i))%Mod;
printf( "%lld\n",ans );
return 0;
}
4. 積性函式與線性篩
聽說 所有積性函式都能線性篩 ,但是時間複雜度不能保證…… Link
線性篩質數
最基礎的一種,保證每個質數只會被最小質因子篩掉。
Code
int pri[N],tot,is[N]; //is[i]為1的表示不是質數
void sieve()
{
is[1]=1;
for ( int i=2; i<=n; i++ )
{
if ( !is[i] ) pri[++tot]=i;
for ( int j=1; j<=tot && i*pri[j]<=n; j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j]==0 ) break;
}
}
}
線性篩尤拉函式
定義:\(1\sim n\) 中與 \(n\) 互質的數的個數。
性質:對於一個數 \(i\) ,如果 \(pri[j]|i\) ,那麼有 \(phi[i\times pri[j]]=phi[i]\times pri[j]\) .
Proof
如果一個數 \(x\) 與 \(i\) 互質,那麼顯然,\(x+i\) 依然與 \(i\) 互質。
由此可得,\(x,x+i,\dots,x+i\times (pri[j]-1)\) 都與 \(i\) 互質,那麼與 \(i\times pri[j]\) 互質的至少有 \(phi[i]\times pre[j]\) 個。而與 \(i\) 不互質的數 \(y\) ,加上 \(i\) 也不會與 \(i\) 互質,同理可得,不會出現新的與 \(i\) 互質的數,得證。
Code
int phi[N],pri[N],tot,is[N];
void sieve()
{
is[1]=phi[1]=1;
for ( int i=2; i<=n; i++ )
{
if ( !is[i] ) pri[++tot]=i,phi[i]=i-1; //質數除了本身都互質
for ( int j=1; j<=tot && i*pri[j]<=n; j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j] ) phi[i*pri[j]]=phi[i]*phi[pri[j]]; //積性函式定義
else { phi[i*pri[j]]=phi[i]*pri[j]; break; } //上述性質
}
}
}
線性篩約數個數
令 \(d[i]\) 為 \(i\) 的約數個數。
將每個數 \(x\) 表示成算術基本定理的形式:\(x=\prod p_i^{a_i}\) ,那麼考慮每個質因子所取的冪次,有
\[d[x]=\prod_{i=1}^k(a_i+1) \]維護每個數最小質因子的出現次數(\(a_1\) )即可。
Code
int d[N],a[N],pri[N],tot,is[N];
void sieve()
{
is[1]=d[1]=1;
for ( int i=2; i<=n; i++ )
{
if ( !is[i] ) pri[++tot]=i,d[i]=2,a[i]=1; //質數的約數個數為2
for ( int j=1; j<=tot && i*pri[j]<=n; j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j] ) d[i*pri[j]]=d[i]*d[pri[j]],a[i*pri[j]]=1;
//新的質因數,i*pri[j]的最小質因數為pri[j],冪次為1
else { d[i*pri[j]]=d[i]/(a[i]+1)*(a[i]+2); a[i*pri[j]]=a[i]+1 ;break; }
//最小質因數 pri[j],也就是 a[i] 的冪次又多了1,除去之前乘的再乘上新的冪次
}
}
}
線性篩約數和
記 \(\sigma(i)\) 表示 \(i\) 的約數和。考慮每個質因數取的冪次,同樣有:(這個展開應該就能理解了吧)
\[\sigma(i)=\prod_{i=1}^k(\sum_{j=0}^{a_i}p_i^j) \]令 \(low(i)\) 表示 \(i\) 的最小質因數的指數冪,即 \(p_1^{a_1}\) ;\(sum(i)\) 表示 \(i\) 的最小質因數對答案的貢獻,\(\sum_{j=0}^{a_1}p_1^j\) .
(小心爆 int
/xyx)
Code
int low[N],sum[N],sigma[N],pri[N],tot,is[N];
void sieve()
{
is[1]=low[1]=sum[1]=sigma[1]=1;
for ( int i=2; i<=n; i++ )
{
if ( !is[i] ) low[i]=pri[++tot]=i,sum[i]=sigma[i]=i+1;
for ( int j=1; j<=tot && i*pri[j]<=n; j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j]==0 )
{
low[i*pri[j]]=low[i]*pri[j];
sum[i*pri[j]]=sum[i]+low[i*pri[j]];
sigma[i*pri[j]]=sigma[i]/sum[i]*sum[i*pri[j]];
break;
}
//不能整除,也就是 pri[j] 是 i*pri[j] 的最小質因數
low[i*pri[j]]=pri[j];
sum[i*pri[j]]=pri[j]+1;
sigma[i*pri[j]]=sigma[i]*sigma[pri[j]];
}
}
}
線性篩一般積性函式
前提 :能快速計算出 \(f(1),f(prime),f(prime^k)\) .(也就是質因數個數小於等於 \(1\) 的所有數的函式值)
計算 :假設已經完成了上述值的計算。顯然,含有至少兩個質因數的數一定可以被分解成兩個互質且不為 1 的數的乘積。根據積性函式的定義,可以用 \(f(xy)=f(x)f(y)\) 來得出相應的函式值。
現在來考慮具體怎麼篩。
思考線性篩的之所以線性的原因,是因為 每個數都只被最小質因子篩了一次 ,也就是每個形如 \(i\times pri[j]\) 的數會在 \(i\) 的時候被 \(i\) 乘上 \(pri[j]\) 篩掉,且若將 \(i\) 唯一分解,有 \(pri[j]\leq p_1\) .(顯然,否則在 \(p_1\) 的時候就會 break 掉)
分類討論:
-
\(pri[j]<p_1\)
由於 \(\gcd(pri[j],i)=1\) ,直接計算即可 \(f(i\times pri[j])=f(i)\times f(pri[j])\) .
-
\(pri[j]=p_1\)
這時候需要記錄 \(i\) 中最小質因子的冪次,也就是 \(low[i]=p_1^{a_1}\) .
顯然,\(i/low[i]\) 中的最小質因數一定大於 \(pri[j]\) ,那麼 \(\gcd(i/low[i],low[i]\times pri[j])=1\) ,
有 \(f(i\times pri[j])=f(i/low[i])\times f(low[i]\times pri[j])\) .
特別地,當 \(low[i]=i\) 時,\(i\) 只有一個質因數,要利用前面的前提特殊計算。
Code
void sieve()
{
is[1]=low[1]=1; f[1]=對1直接定義;
for ( ll i=2; i<=n; i++ )
{
if ( !is[i] ) low[i]=pri[++tot]=i,f[i]=對質數直接定義;
for ( ll j=1; j<=tot && i*pri[j]<=n; j++ )
{
is[i*pri[j]]=1;
if ( i%pri[j]==0 )
{
low[i*pri[j]]=low[i]*pri[j];
if ( low[i]==i ) f[i*pri[j]]=對質數的若干次冪進行定義(一般由f[i]遞推);
else f[i*pri[j]]=f[i/low[i]]*f[low[i]*pri[j]];
break;
}
low[i*pri[j]]=pri[j]; f[i*pri[j]]=f[i]*f[pri[j]];
}
}
}
What's more
對於某些形如Dirichlet卷積形式,但 \(f(x)\) 或 \(g(x)\) 不是積性函式,資料範圍較小可以暴力篩,列舉一個 \(d\) 計算對哪些 \(x\) 做貢獻,較大的情況下可以考慮相關性質。
題意:求
\[\sum_{i=1}^n\sum_{j=1}^m \gcd(i,j)^k\bmod 1e9+7 \]思路:不妨設 \(n\leq m\) .
\[\sum_{i=1}^n\sum_{j=1}^m \gcd(i,j)^k\bmod 1e9+7 =\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^nd^k[\gcd(i,j)=1]\\ =\sum_{d=1}^nd^k\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor n/d\rfloor}\sum_{x|i,x|j}\mu(x)\\ =\sum_{d=1}^nd^k\sum_{x=1}^{\lfloor n/d\rfloor}\mu(x)\lfloor \frac{n}{dx}\rfloor\lfloor\frac{m}{dx}\rfloor \]令 \(T=dx\) ,有:
\[\sum_{T=1}^n\lfloor n/T\rfloor \lfloor m/T\rfloor\sum_{d|T}d^k\mu(T/d) \]令 \(g(T)=\sum_{d|T}d^k\mu(T/d)\) ,預處理 \(g(T)\) 和字首和(線篩),分塊即可。
Code
const int inf=0x7f7f7f7f,N=5e6+10,mod=1e9+7,M=5e6;
int pri[N],u[N],g[N],f[N],T,k,tot,n,m;
bool vis[N];
int power( int a,int b )
{
int res=1;
for ( ; b; b>>=1,a=1ll*a*a%mod )
if ( b&1 ) res=1ll*res*a%mod;
return res;
}
void init()
{
f[1]=1;
for ( int i=2; i<=M; i++ )
{
if ( !vis[i] )
{
pri[++tot]=i; g[tot]=power( i,k ); f[i]=(g[tot]-1+mod)%mod;
}
for ( int j=1; j<=tot && i*pri[j]<=M; j++ )
{
int tmp=i*pri[j];
vis[tmp]=1;
if ( i%pri[j]==0 )
{
f[tmp]=1ll*f[i]*g[j]%mod; break;
}
f[tmp]=1ll*f[i]*f[pri[j]]%mod;
}
}
for ( int i=1; i<=M; i++ )
f[i]=(f[i]+f[i-1])%mod;
}
int main()
{
scanf( "%d%d",&T,&k );
init();
while ( T-- )
{
scanf( "%d%d",&n,&m );
int maxx=min( n,m ),pos,ans=0;
for ( int i=1; i<=maxx; i=pos+1 )
{
pos=min( n/(n/i),m/(m/i) );
ans=(ans+1ll*(f[pos]-f[i-1]+mod)%mod*(n/i)%mod*(m/i)%mod)%mod;
}
printf( "%d\n",(ans+mod)%mod );
}
}
學習材料
其實本文是一個大集合qwq(
窩tcl,年底才學 神George1123
年初就會的東西qaq