莫比乌斯反演专题(进阶篇)
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\)
求
化简
其中\(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
设\(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
\(f(x)\)表示\(x\)的质因子的最高次幂
后面的东西显然可以\(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 数字表格
比较奇怪的题目
求
\(f(x)\)为斐波那契数列
开始反演
单独看分母,我们知道
喜闻乐见,枚举\(T\)
也就是
设\(F(n)=\prod_{d|n}f(d)^{\mu(\frac{n}{d})}\),这可以\(O(n\log n)\)预处理
则
因为指数的变化量根据整除分块只有\(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\)的素因子个数
设\(S(i,j)=\sum_{d|i}[w(d)==j]\mu(\frac{i}{d})\)
这个可以调和级数\(O(n\log n)\)预处理
那么
再对这个做前缀和就可以了
#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;
}

浙公网安备 33010602011771号