1. 程式人生 > 其它 >Python中的map函式,lambda表示式,filter函式的用法

Python中的map函式,lambda表示式,filter函式的用法

莫比烏斯反演學習筆記

前置知識

  • \(x|y:\) \(x\)\(y\) 的因數。

  • \([S]:\)\(S\) 為真,\([S]=1\),若 \(S\) 為假,則 \([S]=0\)(如 \([gcd(11,45)=14]=0\))。

  • \(\sum:\) 求和符號。

  • \(\prod:\) 連乘符號。

  • \(n=\prod\limits_{i=1}^{k}p_i^{q_i}:\)\(n\) 分解質因數。即預設 \(p_i\) 兩兩不同且為質數。

基本定義

先介紹 \(\mu\) 函式:

  • \(\mu(1)=1\)

  • \(i\) 含有平方因子,\(\mu(i)=0\)

  • \(i\)奇數不同的質因數的乘積,\(\mu(i)=-1\)

  • \(i\)偶數不同的質因數的乘積,\(\mu(i)=1\)

容易發現,\(\mu\) 可以進行線性篩:

Miu[1]=1;//Miu(1)=1
for(int i=2;i<=m;i++){
	if(!vis[i]) Prime[++cnt]=i,Miu[i]=-1;
	for(int j=1,x;j<=cnt&&(x=i*Prime[j])<=m;j++){
		vis[x]=1;
		if(i%Prime[j]==0){
			Miu[x]=0;
			//x含有平方因子 Prime[j]*Prime[j],Miu(x)=0
			break;
		}
		Miu[x]=-Miu[i];
		//x=i*Prime[j],多乘了一個不同的質數,所以Miu(x)=Miu(i)*-1
	}
}

性質

莫比烏斯函式的精髓是以下結論:

\[f(n)=\sum\limits_{d|n}\mu(d),f(n)=[n=1] \]
  • 證明:

    隨便手推一下就可以得到 \(f(1)=1\)

    如果 \(d\) 含有平方因子,根據定義有 \(\mu(d)=0\),對答案沒有貢獻。因此,\(n\) 中所含的平方因子也是沒有意義的。所以若 \(n=\prod\limits_{i=1}^{k}p_i^{q_i},n'=\prod\limits_{i=1}^{k}p_i\),有 \(f(n)=f(n')\)\(n\)\(n'\) 多的因子都是平方因子)。

    考慮證明對於 \(n=\prod\limits_{i=1}^{k}p_i(n\not=1)\)

    \(f(n)=0\)

    根據約數個數定理,\(n\) 的因子有 \(2^k\) 個。因為 \(n\not=1\),有 \(k \ge 1\),所以在 \(n\) 的因子中,有 \(2^{k-1}\) 個因子是 偶數個不同的質因數的乘積(即 \(\mu(x)=1\)),\(2^{k-1}\) 個因子是 奇數個不同的質因數的乘積(即 \(\mu(x)=-1\))。兩兩抵消,\(f(n)=0\)

    證畢。

至於那個莫比烏斯反演的經典式子,本質上就是 \(\sum\limits_{d|n}\mu(d)=[n=1]\) 的推論罷了。

應用

莫比烏斯反演本質上是利用 \(\mu\) 函式的性質,通過湊出 形如 \([n=1]\) 的項來對式子進行轉化,然後再通過改變列舉順序、設參等方法將其變為可以用其他演算法快速求解的式子(如線性篩,整除分塊,杜教篩等)。

注意,題目一般運用的轉換是 \([n=1]=\sum\limits_{d|n}\mu(d)\),而不是 \(\sum\limits_{d|n}\mu(d)=[n=1]\)

[HAOI2011]Problem b

題目大意:

給定 \(a,b,c,d,k\),求 \(\sum\limits_{i=a}^b\sum\limits_{j=c}^d[gcd(i,j)=k]\)

先轉換為 \(\sum\limits_{i=1}^b\sum\limits_{j=1}^d[gcd(i,j)=k]\),推就完事了(最後容斥計算答案即可)。

\[\sum\limits_{i=1}^b\sum\limits_{j=1}^d[gcd(i,j)=k] \] \[=\sum\limits_{i=1}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{d}{k}\rfloor}[gcd(i,j)=1] \]

通過轉換,我們湊出了 \([n=1]\) 的形式。帶入 \(\mu\) 函式:

\[=\sum\limits_{i=1}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{d}{k}\rfloor}\sum\limits_{d|gcd(i,j)}\mu(d) \]

先列舉 \(gcd(i,j)\)

\[=\sum\limits_{g=1}^{min(b,d)}\mu(g)\sum\limits_{g|i}^{b}\sum\limits_{j|g}^{d}1 \] \[=\sum\limits_{g=1}^{min(b,d)}\mu(g)\lfloor{\frac{b}{j}}\rfloor\lfloor{\frac{d}{j}}\rfloor \]

線性篩出 \(\mu\) 函式,維護字首和後用值域分塊求解即可。時間複雜度 \(O(\sqrt b+\sqrt d+\max(b,d))\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=50010;
inline int read(){
	int x=0;
	char c=getchar();
	for(;!(c>='0'&&c<='9');c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+c-'0';
	return x;
}
bool vis[maxn];
int Prime[maxn],cnt;
int Miu[maxn],n,k;
void Init(int n){
	Miu[1]=1;//線性篩
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			Prime[++cnt]=i;
			Miu[i]=-1;
		}
		for(int j=1,x;j<=cnt&&(x=i*Prime[j])<=n;j++){
			vis[x]=1,Miu[x]=-Miu[i];
			if(i%Prime[j]==0){
				Miu[x]=0;
				break;
			}
		}
	}
	for(int i=1;i<=n;i++) Miu[i]+=Miu[i-1];
}
ll Query(int n,int m,ll sum=0){
  //整除分塊
	for(int l=1,r=1;l<=min(n/k,m/k);l=r+1){
		r=min(n/(n/l),m/(m/l));
		sum+=(ll)(Miu[r]-Miu[l-1])*(n/(k*l))*(m/(k*l));
	}
	return sum;
} 
int main(){
	n=read();
	Init(5e4);
	int a,b,c,d;
	while(n--){
		a=read(),b=read(),c=read(),d=read(),k=read();
		printf("%lld\n",Query(b,d)-Query(a-1,d)-Query(b,c-1)+Query(a-1,c-1));	
	}
	return 0;
}

[國家集訓隊]Crash的數字表格 / JZPTAB

題目大意:

給定 \(n,m\),求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m lcm(i,j)\)\(20101009\) 取模的結果。

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^m lcm(i,j) \] \[=\sum\limits_{i=1}^n\sum\limits_{j=1}^m \frac{ij}{gcd(i,j)} \]

先列舉 \(gcd(i,j)\)

\[=\sum\limits_{k=1}^{min(n,m)}\sum\limits_{i=1}^n\sum\limits_{j=1}^m \frac{ij}{k}\times[gcd(i,j)=k] \] \[=\sum\limits_{k=1}^{min(n,m)}\frac{1}{k}\sum\limits_{i=1}^ni\sum\limits_{j=1}^mj \times[gcd(i,j)=k] \] \[=\sum\limits_{k=1}^{min(n,m)}\frac{1}{k}\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}ki\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}kj \times[gcd(i,j)=1] \] \[=\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}i\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}j \times[gcd(i,j)=1] \]

再次湊出了形如 \([n=1]\) 的式子,帶入 \(\mu\) 函式:

\[=\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}i\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}j \sum\limits_{d|gcd(i,j)}\mu(d) \]

先列舉 \(d\)

\[=\sum\limits_{d=1}^{min(n,m)}\mu(d)\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{d|i}^{\lfloor\frac{n}{dk}\rfloor}i\sum\limits_{d|j}^{\lfloor\frac{m}{k}\rfloor}j \] \[=\sum\limits_{d=1}^{min(n,m)}\mu(d)\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{i=1}^{\lfloor\frac{n}{dk}\rfloor}di\sum\limits_{j=1}^{\lfloor\frac{m}{dk}\rfloor}dj \] \[=\sum\limits_{d=1}^{min(n,m)}d^2\mu(d)\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{i=1}^{\lfloor\frac{n}{dk}\rfloor}i\sum\limits_{j=1}^{\lfloor\frac{m}{dk}\rfloor}j \] \[=\sum\limits_{d=1}^{min(n,m)}d^2\mu(d)\sum\limits_{k=1}^{min(n,m)}k\frac{\lfloor\frac{n}{dk}\rfloor(\lfloor\frac{n}{dk}\rfloor+1)}{2}\frac{\lfloor\frac{m}{dk}\rfloor(\lfloor\frac{m}{dk}\rfloor+1)}{2} \] \[=\sum\limits_{d=1}^{min(n,m)}d^2\mu(d)\sum\limits_{k=1}^{min(n,m)}k\frac{\lfloor\frac{n}{dk}\rfloor\lfloor\frac{m}{dk}\rfloor(\lfloor\frac{n}{dk}\rfloor+1)(\lfloor\frac{m}{dk}\rfloor+1)}{4} \]

考慮先列舉 \(t=dk\)

\[=\sum\limits_{t=1}^{min(n,m)}\frac{\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor(\lfloor\frac{n}{t}\rfloor+1)(\lfloor\frac{m}{t}\rfloor+1)}{4}\sum\limits_{d|t}d^2\mu(d)\frac{t}{d} \] \[=\sum\limits_{t=1}^{min(n,m)}t\frac{\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor(\lfloor\frac{n}{t}\rfloor+1)(\lfloor\frac{m}{t}\rfloor+1)}{4}\sum\limits_{d|t}d\mu(d) \]

\(F(n)=\sum\limits_{d|n}d\mu(d)\)

\[=\sum\limits_{t=1}^{min(n,m)}\frac{\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor(\lfloor\frac{n}{t}\rfloor+1)(\lfloor\frac{m}{t}\rfloor+1)}{4}tF(t) \]

線性篩出 \(F\) 後用整除分塊即可。時間複雜度 \(O(\sqrt{n}+\sqrt{m}+\max(n,m))\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000010;
const int mod=20101009;
bool vis[maxn];
int n,m,Prime[maxn],f[maxn],g[maxn],cnt;
int fastpow(int n,int m){
	int a=n,S=1;
	while(m){
		if(m&1) S=S*1ll*a%mod;
		a=a*1ll*a%mod,m>>=1;
	}
	return S;
}
int Divid(int a,int b){return fastpow(b,mod-2)*1ll*a%mod;}
void init(int N=max(n,m)){
	int tmp,c;//線性篩
	f[1]=1,g[0]=1;
	for(int i=2;i<=N;i++){
		if(!vis[i])
			Prime[++cnt]=i,f[i]=mod-i+1;
		for(int j=1,x;j<=cnt&&Prime[j]*1ll*i<=N;j++){
			vis[x=Prime[j]*i]=1;
			if(i%Prime[j]==0){
				f[x]=f[i];
				break;
			}
			f[x]=(f[i]-f[i]*1ll*Prime[j]%mod+mod)%mod;
		}
	}
	for(int i=1;i<=N;g[i]=g[i-1]*1ll*i%mod,i++)
		f[i]=(Divid(f[i],4)*1ll*i%mod+f[i-1])%mod;	
}
int Query(int sum=0){
	int tmp;.//整除分塊
	for(int l=1,r=1;l<=min(n,m);l=r+1){
		r=min(n/(n/l),m/(m/l));
		tmp=((n/l)*(n/l+1ll)%mod)*((m/l)*(m/l+1ll)%mod)%mod;
		tmp=tmp*1ll*(f[r]-f[l-1]+mod)%mod;
		if((sum+=tmp)>=mod) sum-=mod;
	}
	return sum;
}
int main(){
	scanf("%d%d",&n,&m),init();
	printf("%d\n",Query());
	return 0;
}