Loading

莫比乌斯反演专题(进阶篇)

51nod 1675 序列变换

我们构造两个新的数列\(A,B\)
\(A_x=a_{b_x},B_x=b_{a_x}\)
则题目给的条件等价于
\(gcd(i,j)==1,A_i=B_j\)\(i,j\)个数
我们考虑设\(f(x)\)表示\(gcd(i,j)==x,A_i=B_j\)\(i,j\)个数
\(F(x)\)表示\(x|gcd(i,j),A_i=B_j\)\(i,j\)个数
那么我们要求的就是\(f(1)\)
那么\(F(n)=\sum_{n|d}f(d)\)
根据莫比乌斯倍数反演公式
\(f(n)=\sum_{n|d}F(d)\mu(\frac{d}{n})\)
我们考虑如何求出\(F(x)\)
观察到\(x|i,x|j\),也就是\(i,j\)都是\(x\)的倍数
那么我们可以先在A数列中把下标是\(x\)的倍数的位置的\(A_i\)加入到桶中,然后我们在\(B\)中枚举下标是\(x\)的倍数的位置,看看桶中有多少\(B_j\),就行了,复杂度为调和级数\(O(nlogn)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+7;
int a[N],b[N];
int mu[N],prime[N],tot=0;
int v[N];
void init(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
}
int A[N],B[N];
int n;
int t[N];
int main()
{
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	cin>>n;
	init(n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	scanf("%d",&b[i]);
	for(int i=1;i<=n;i++)
	A[i]=a[b[i]];
	for(int i=1;i<=n;i++)
	B[i]=b[a[i]];
	long long ans=0;
	for(int i=1;i<=n;i++)
	{
		LL F=0;
		for(int j=i;j<=n;j+=i)
		t[A[j]]++;
		for(int j=i;j<=n;j+=i)
		F+=t[B[j]];
		for(int j=i;j<=n;j+=i)
		t[A[j]]--;
		ans+=1ll*mu[i]*F;
	} 
	cout<<ans;
	return 0;
}

SDOI2014 数表

\(f(x)\)表示\(x\)的因数个数
那么题目有Q次询问,每次给出\(n,m,a\)

\[\sum_{i=1}^n\sum_{j=1}^mf(gcd(i,j)),f(gcd(i,j))\leq a \]

化简

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j,f(d)\leq a}[gcd(i,j)==d]f(d) \]

\[\sum_{d=1}^n[f(d)\leq a]\sum_{d|i}^n\sum_{d|j}^m[gcd(i,j)==d]f(d) \]

\[\sum_{d=1,f(d)\leq a}^nf(d)\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}[gcd(i,j)==1] \]

\[\sum_{d=1,f(d)\leq a}^nf(d)\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}\sum_{k|i,k|j}\mu(k) \]

\[\sum_{d=1,f(d)\leq a}^nf(d)\sum_{k=1}^{\lfloor \frac{n}{d}\rfloor}\mu(k)\sum_{i=1}^{\lfloor \frac{n}{dk}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{dk}\rfloor}1 \]

\[\sum_{d=1,f(d)\leq a}^nf(d)\sum_{k=1}^{\lfloor \frac{n}{d}\rfloor}\mu(k)\lfloor \frac{n}{dk}\rfloor\lfloor \frac{m}{dk}\rfloor \]

\[\sum_{T=1}^n\lfloor \frac{n}{T}\rfloor\lfloor \frac{m}{T}\rfloor \sum_{d|T,f(d)\leq a}^nf(d)\mu(\frac{T}{d}) \]

其中\(f\)是积性函数可以线筛预处理
如果没有a的限制就直接整除分块\(O(\sqrt n)\)做就可以了
但是有\(a\)的限制,所以有的\(d\)是不合法的
我们考虑将询问离线,然后按照\(a\)排序
这样可以保证一个数如果在某一时刻产生了贡献,那么他的贡献就不会消除,均摊下来就是\(O(n)\)
所以我们将所有数按照\(f\)排序,然后我们枚举新加入的那些\(d\)
然后枚举他的倍数\(T\),加上\(d对T\)的贡献,因为整除分块需要前缀和,所以用树状数组加上贡献
时间复杂度\(O(q\sqrt n \log n+n\log^2 n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 1e5+10;
LL t[N],g[N],f[N];
LL v[N],prime[N],tot=0;
LL w[N];
LL mu[N]; 
bool cmp(LL x,LL y)
{
	return f[x]<f[y];
}
const LL mod = (1ll<<31); 
void init(LL n)
{
	f[1]=1;
	mu[1]=1;
	for(LL i=2;i<=n;i++)
	{
		if(!v[i])
		{
			prime[++tot]=i;
			v[i]=i;
			f[i]=1+i;
			g[i]=i;
			t[i]=1+i;
			mu[i]=-1;
		}
		for(LL j=1;j<=tot;j++)
		{
			if(v[i]<prime[j]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				g[i*prime[j]]=g[i]*prime[j];
				t[i*prime[j]]=t[i]+g[i*prime[j]];
				f[i*prime[j]]=f[i]/t[i]*t[i*prime[j]];
				break;
			}
			else 
			{
				mu[i*prime[j]]=mu[i]*mu[prime[j]];
				g[i*prime[j]]=prime[j];
				t[i*prime[j]]=prime[j]+1;
				f[i*prime[j]]=f[i]*f[prime[j]];
			}
		}
	}
	for(LL i=1;i<=n;i++)
	w[i]=i;
	sort(w+1,w+n+1,cmp);
}
LL tree[N];
LL lowbit(LL x)
{
	return x&(-x);
}
void add(LL x,LL v)
{
	for(LL i=x;i<=1e5;i+=lowbit(i))
	tree[i]+=v;
}
LL ask(LL x)
{
	LL res=0;
	for(LL i=x;i;i-=lowbit(i))
	res+=tree[i];
	return res;
}
struct Query
{
	LL n,m,a;
	LL id;
}q[N];
bool cmp2(Query a,Query b)
{
	return a.a<b.a;
}
LL ans[N];
LL calc(LL n,LL m)
{
	if(n>m) swap(n,m);
	LL l=1,r;
	LL ans=0;
	for(;l<=n;l=r+1)
	{
		r=min(n/(n/l),m/(m/l));
		ans=(ans+(ask(r)-ask(l-1))*1ll*(n/l)*(m/l)); 
	}
	return ans;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	init(1e5);
	LL Q;
	cin>>Q;
	for(LL i=1;i<=Q;i++)
	scanf("%lld %lld %lld",&q[i].n,&q[i].m,&q[i].a);
	for(LL i=1;i<=Q;i++)
	q[i].id=i;
	sort(q+1,q+Q+1,cmp2);
	LL j=1;
	for(LL i=1;i<=Q;i++)
	{
		while(f[w[j]]<=q[i].a&&j<=1e5)
		{
			for(LL k=w[j];k<=1e5;k+=w[j])
			{
				add(k,f[w[j]]*mu[k/w[j]]);	
			}
			j++;
		}
		ans[q[i].id]=calc(q[i].n,q[i].m);
	}
	for(LL i=1;i<=Q;i++)
	printf("%lld\n",ans[i]%mod);
	return 0;
}

BZOJ3561 DZY Loves Math VI

\[\sum_{i=1}^n\sum_{j=1}^mlcm(i,j)^{gcd(i,j)} \]

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

\[=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}(\frac{ij}{d})^{d}[gcd(i,j)==d] \]

\[=\sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}(ijd)^{d}[gcd(i,j)==d] \]

\[=\sum_{d=1}^nd^d\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}i^d\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}j^d[gcd(i,j)==d] \]

\[=\sum_{d=1}^nd^d\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}i^d\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}j^d\sum_{k|i,k|j}\mu(k) \]

\[=\sum_{d=1}^nd^d\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)k^{2d}\sum_{i=1}^{\lfloor \frac{n}{dk} \rfloor}i^d\sum_{j=1}^{\lfloor \frac{n}{dk} \rfloor}j^d \]

\(S(x)=\sum_{i=1}^xi^d\)
那么预处理就可以做到\(O(nlogn)\)
接着枚举\(d\)\(k\),经典调和级数\(O(nlogn)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5+7;
LL mu[N];
int prime[N],tot=0;
int v[N];
void init(int n)
{
	mu[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			mu[i]=-1;
			prime[++tot]=i;
		}
		for(int j=1;j<=tot;j++)
		{
			if(v[i]<prime[j]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
}
const int mod = 1e9+7;
LL Pow(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
}
LL pw[N];
LL S[N];
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	int n,m;
	cin>>n>>m;
	if(n>m) swap(n,m);
	init(n);
	LL ans=0;
	for(int i=1;i<=m;i++)
	pw[i]=1;
	for(int d=1;d<=n;d++)
	{
		LL p=Pow(d,d);
		int I=(n/d),J=(m/d);
		for(int i=1;i<=J;i++)
		{
			pw[i]=1ll*pw[i]*i%mod;
			S[i]=(S[i-1]+pw[i])%mod;
		}
		for(int x=1;x<=I;x++)
		ans=(ans+1ll*p*mu[x]%mod*pw[x]%mod*pw[x]%mod*S[I/x]%mod*S[J/x]%mod)%mod;
 	} 
 	cout<<ans;
	return 0;
}

[BZOJ3309]DZY Loves Math

\[\sum_{i=1}^n\sum_{j=1}^mf(gcd(i,j)) \]

\(f(x)\)表示\(x\)的质因子的最高次幂

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}[gcd(i,j)==d]f(d) \]

\[\sum_{d=1}^nf(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}[gcd(i,j)==1] \]

\[\sum_{d=1}^nf(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}\sum_{k|i,k|j}\mu(k) \]

\[\sum_{d=1}^nf(d)\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)\sum_{i=1}^{\lfloor \frac{n}{dk} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{dk}\rfloor}1 \]

\[\sum_{d=1}^nf(d)\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)\lfloor \frac{n}{dk} \rfloor\lfloor \frac{m}{dk}\rfloor \]

\[\sum_{T=1}^n\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T}\rfloor\sum_{d|T}^nf(d)\mu(\frac{T}{d}) \]

后面的东西显然可以\(O(nlogn)\)算,但是\(n\)\(10^7\)……
我们考察一下后面函数的性质
\(f(T)=a\)\(T\)\(k\)个不同质因子,\(T有q\)个因子的指数等于\(a\)
我们考察一下什么时候\(\mu(T/d)\)不为0
也就是\(f(d)\)\(a\)\(a-1\),否则\(\mu(T/d)\)就有因子的指数大于2,就是0了
我们分类讨论一下
\(q==k\)
也就是所有因子的指数都为\(a\)
那么若\(f(d)==a\)
则贡献为\(a\sum_{f(d)==a}\mu(T/d)\)
那么我们枚举\(d\)中有i个位置是\(a\)\(k-i\)个位置是\(a-1\),那么\(T\d\)中只有那\(k-i\)个位置有值
也就是\(\sum_{i=1}^kC_k^i(-1)^{k-i}\),之所以因为\(f(d)=a\)所以至少有一个\(a\),因此从i开始枚举,根据二项式定理,\(=(1+(-1))^k-(-1)^k=a(-1)^{k+1}\)
那么若\(f(d)==a-1\)
则d中所有的位置都必须是\(a-1\),则答案就是\((a-1)(-1)^k\)
所以当\(k==q\)的总答案为\(a(-1)^{k+1}+(a-1)(-1)^k=(-1)^k\)
类似的若\(k\geq q\),则可以算出答案为0
所以我们得出以下结论
\(T\)中不是所有位置的指数都为a,则\(F(T)=0\)
\(T\)有奇数个不同质因子,则\(F(T)=1\)
否则\(F(T)=-1\)
我们可以用线性筛晒出来每个数的最高次幂,不同质因子个数,最小素因子的次幂,次幂等于最高次幂的素因子个数,然后就能求出每个数的\(F\),接着只要求出前缀和整除分块就行了
注意卡常

SDOI2017 数字表格

比较奇怪的题目

\[\prod_{i=1}^n\prod_{j=1}^mf(gcd(i,j)) \]

\(f(x)\)为斐波那契数列
开始反演

\[\prod_{i=1}^n\prod_{j=1}^m\prod_{d|i,d|j}[gcd(i,j)==d]f(d) \]

\[\prod_{d=1}^nf(d)^{\sum{i=1}^n\sum{j=1}^m[gcd(i,j)==d]} \]

\[\prod_{d=1}^nf(d)^{\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[gcd(i,j)==1]} \]

单独看分母,我们知道

\[\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[gcd(i,j)==1]=\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\mu(k)\lfloor \frac{n}{dk} \rfloor\lfloor \frac{m}{dk} \rfloor \]

喜闻乐见,枚举\(T\)

\[\prod_{T=1}^{n}\prod_{d|T}f(d)^{\mu(\frac{T}{d})\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor} \]

也就是

\[\prod_{T=1}^{n}\prod_{d|T}(f(d)^{\mu(\frac{T}{d})})^{\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor} \]

\(F(n)=\prod_{d|n}f(d)^{\mu(\frac{n}{d})}\),这可以\(O(n\log n)\)预处理

\[ans=\prod_{T=1}^{n}\prod_{d|T}F(T)^{\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor} \]

因为指数的变化量根据整除分块只有\(O(\sqrt n)\)种,所以预处理\(F\)的前缀积,就可以整除分块了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 1e6+7;
const LL mod = 1e9+7;
LL mu[N],v[N],prime[N];
LL tot=0;
LL Pow(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return res;
} 
LL inv[N];
LL f[N];
LL F[N];
void init(LL n)
{
	f[0]=0;
	f[1]=1;
	inv[1]=1;
	for(LL i=2;i<=n;i++)
	{
		f[i]=(f[i-1]+f[i-2])%mod;
		inv[i]=Pow(f[i],mod-2);
	}
	mu[1]=1;
	for(LL i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(LL j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				break;
			}
			else mu[i*prime[j]]=mu[i]*mu[prime[j]];
		}
	}
	for(LL i=1;i<=n;i++)
	F[i]=1;
	for(LL d=1;d<=n;d++)
	{
		for(LL T=d;T<=n;T+=d)
		{
			LL val=(mu[T/d]==1?f[d]:(mu[T/d]==-1?inv[d]:1ll));
			F[T]=1ll*F[T]*val%mod;
		}
	}
	F[0]=1;
	for(LL i=1;i<=n;i++)
	F[i]=1ll*F[i-1]*F[i]%mod;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	init(1e6);
	LL T;
	cin>>T;
	while(T--)
	{
		LL n,m;
		scanf("%lld %lld",&n,&m);
		if(n>m) swap(n,m);
		LL ans=1;
		LL l=1,r;
		for(;l<=n;l=r+1)
		{
			r=(min(n/(n/l),m/(m/l)));
			LL val=F[r]*Pow(F[l-1],mod-2)%mod;
			ans=1ll*ans*Pow(Pow(val,(n/l)),(m/l))%mod; 
		}
		printf("%lld\n",ans); 
	}
	return 0;
} 

[HDU4746]Mophues


\(w(n)\)表示\(n\)的素因子个数

\[\sum_{i=1}^n\sum_{j=1}^m[w(gcd(i,j))\leq p] \]

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j,w(d)\leq p}[gcd(i,j)==d] \]

\[\sum_{d=1,w(d)\leq p}^n\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}[gcd(i,j)==1] \]

\[\sum_{d=1,w(d)\leq p}^n\sum_{k=1}\mu(k)\sum_{i=1}^{\lfloor \frac{n}{dk}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{dk}\rfloor}1 \]

\[\sum_{d=1,w(d)\leq p}^n\sum_{k=1}\mu(k) \lfloor \frac{n}{dk}\rfloor \lfloor \frac{m}{dk}\rfloor \]

\[\sum_{T=1}^n\lfloor \frac{n}{T}\rfloor \lfloor \frac{m}{T}\rfloor\sum_{d|T}^n[w(d)\leq p]\mu(\frac{T}{d}) \]

\(S(i,j)=\sum_{d|i}[w(d)==j]\mu(\frac{i}{d})\)
这个可以调和级数\(O(n\log n)\)预处理
那么

\[\sum_{d|T}^n[w(d)\leq p]\mu(\frac{T}{d})=\sum_{j=0}^pS(T,j) \]

再对这个做前缀和就可以了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N = 5e5+7;
LL S[N][22];
LL v[N],prime[N],tot=0;
LL mu[N];
LL num[N];
void init(LL n)
{
	mu[1]=1;
	for(LL i=2;i<=n;i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			num[i]=1;
			mu[i]=-1;
		}
		for(LL j=1;j<=tot;j++)
		{
			if(prime[j]>v[i]||i*prime[j]>n) break;
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
			{
				num[i*prime[j]]=num[i]+1;
				mu[i*prime[j]]=0;
				break;
			}
			else
			{
				num[i*prime[j]]=num[i]+1;
				mu[i*prime[j]]=mu[i]*mu[prime[j]];
			}
		}
	}
	for(LL d=1;d<=n;d++)
	{
		for(LL T=d;T<=n;T+=d)
		S[T][num[d]]=S[T][num[d]]+mu[T/d];
	}
	for(LL p=1;p<=20;p++)
	for(LL i=1;i<=n;i++)
	S[i][p]=S[i][p-1]+S[i][p];
	for(LL p=0;p<=20;p++)
	for(LL i=1;i<=n;i++)
	S[i][p]=S[i-1][p]+S[i][p]; 
}
int main()
{
	freopen("mophues.in","r",stdin);
	freopen("mophues.out","w",stdout);
	init(5e5);
	LL T;
	cin>>T;
	while(T--)
	{
		LL n,m,p;
		scanf("%lld %lld %lld",&n,&m,&p);
		if(p>20)
		{
			printf("%lld\n",1ll*n*m);
			continue;
		}
		if(n>m) swap(n,m);
		LL ans=0;
		LL l=1,r;
		for(;l<=n;l=r+1)
		{
			r=min(n/(n/l),m/(m/l));
			ans=ans+1ll*(n/l)*(m/l)*(S[r][p]-S[l-1][p]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2022-01-25 10:43  Larunatrecy  阅读(50)  评论(0)    收藏  举报