指路博客:peng-ym 莫比乌斯反演
莫比乌斯函数:
·当 (d=1)时,(mu(d)=1);
·当(d=prod_{i=1}^k p_i)且(p_i)为互异素数时,(mu(d)=(-1)^k)。
·只要当(d)含有任何质因子的幂次大于等于2,则函数值为0。
·对于任意正整数(n),(sum_{d|n}^{} mu(d)=[n = 1])。
·对于任意正整数(n),(sum_{d|n}^{} {frac{mu(d)}d}={frac{phi(n)}n})。
·线筛莫比乌斯函数(顺带欧拉函数:
int pri[maxn+5],mu[maxn+5],phi[maxn];
void init()
{
mu[1]=phi[1]=1;
for(int i=2;i<maxn;i++)
{
if(!pri[i])
{
pri[++pri[0]]=i;phi[i]=i-1;mu[i]=-1;
}
for(int j=1;j<=pri[0]&&i*pri[j]<maxn;j++)
{
pri[i*pri[j]]=1;
if(i%pri[j]==0)
{
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
mu[i*pri[j]]=-mu[i];
}
}
}
莫比乌斯反演
·F(n)和f(n)是定义在非负整数集合上的两个函数,并且满足条件:$$F(n)=sum_{d|n}f(d)$$
那么存在一个结论:$$f(n)=sum_{d|n}μ(d)F(lfloorfrac nd
floor)$$
这个定理就称作莫比乌斯反演定理。
·莫比乌斯反演有另外的一种形式,当F(n)和f(n)满足:$$F(n)=sum_{n|d}f(d)$$
可以推出:$$f(n)=sum_{n|d}μ(frac dn)F(d)$$
例题
-
题目描述:
给定N, M,求1<=x<=N, 1<=y<=M且 gcd(x, y)为质数的(x, y)有多少对
-
输入格式:
第一行一个整数T 表述数据组数
接下来T行,每行两个正整数,表示N, M
-
数据范围:
T = 10000;N, M <= 10000000
-
输出格式:
T行,每行一个整数
指路博客:peng-ym YY的GCD
-
等效: (Ans=sum_{i=1}^n sum_{j=1}^m [gcd(x,y)=prim])
-
设(f(d))为(gcd(i,j)=d)的个数,(F(n))为 (gcd(i,j)=n)和(n)的倍数 的个数,即:
[f(d)=sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=d] ][F(n)=sum_{n|d} f(d)=lfloorfrac Nn floorlfloorfrac Mn floor ]由莫比乌斯反演:
[f(n)=sum_{n|d}mu(lfloorfrac dn floor)F(d) ] -
化简公式:
[Ans=sum_{pin prim} sum_{i=1}^n sum_{j=1}^m[gcd(i,j)=p] ]把(f(p))代入得:
[egin{align*} Ans & =sum_{pin prim}f(p)\ & =sum_{pin prim}sum_{n|d}mu(lfloorfrac dn floor)F(d) end{align*} ]换一个枚举项,枚举(lfloor frac dp floor):
[egin{align*} Ans & =sum_{pin prim}sum_{d=1}^{min(lfloor frac np floor,lfloor frac mp floor)}mu(d)F(dp)\ & =sum_{pin prim}sum_{d=1}^{min(lfloor frac np floor,lfloor frac mp floor)} mu(d)lfloor frac np floorlfloor frac mp floor end{align*} ]将dp换成T:
[egin{align*} Ans & =sum_{T=1}^{min(n,m)}sum_{t|T,tin prim}mu(lfloor frac Tt floor)lfloorfrac nT floorlfloor frac mT floor\ & =sum_{T=1}^{min(n,m)}lfloorfrac nT floorlfloor frac mT floor(sum_{t|T,tin prim}mu(lfloor frac Tt floor);) end{align*} ] -
之后整除分块
-
代码:
#include<bits/stdc++.h> #define debug1 printf("!"); #define debug2 puts("#"); using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn=1e7; const int mod=6893911; const int inf=0x3f3f3f3f; template<typename T>inline void read(T&x) { char c; while(!isdigit(c=getchar()));x=c-'0'; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-'0'; } ll sum[maxn+5]; int mu_pfac[maxn+5]; int mu[maxn+5]; int pri[maxn+5],cnt; inline void init() { mu[1]=1; for(int i=2;i<=maxn;i++) { if(!pri[i]) { pri[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++) { pri[i*pri[j]]=1; if(i%pri[j]==0)break; mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=maxn;i++) for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++) mu_pfac[i*pri[j]]+=mu[i]; for(int i=1;i<=maxn;i++) sum[i]=sum[i-1]+(ll)mu_pfac[i]; } inline ll solve(int n,int m) { if(n>m)swap(n,m); ll ans=0; for(int l=1,r;l<=n;l=r+1) { r=min(n/(n/l),m/(m/l)); ans+=1ll*(n/l)*(m/l)*(sum[r]-sum[l-1]); } return ans; } int main() { init(); int T; read(T); while(T--) { int n,m; read(n);read(m); printf("%lld ",solve(n,m)); } }
-
题目描述:
给定a, b,d,求1<=x<=a, 1<=y<=b且 gcd(x, y)=d的(x, y)有多少对
-
输入格式:
第一行一个整数T 表述数据组数
接下来T行,每行三个正整数,表示a,b,d
-
数据范围:
(1leq Tleq 5 imes 10^4,1leq dleq a,bleq 5 imes 10^4)
-
输出格式:
T行,每行一个整数
指路博客:peng-ym ZAP-Queries
-
等效: (Ans=sum_{i=1}^a sum_{j=1}^b [gcd(x,y)=d])
-
设(f(d))为(gcd(i,j)=d)的个数,(F(n))为 (gcd(i,j)=n)和(n)的倍数 的个数,即:
[f(d)=sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=d] ][F(n)=sum_{n|d} f(d)=lfloorfrac an floorlfloorfrac bn floor ]由莫比乌斯反演:
[f(n)=sum_{n|d}mu(lfloorfrac dn floor)F(d) ]为了不混淆,将题意的d换成n,把(f(n))代入得:
[egin{align*}Ans & =f(n)\ & =sum_{n|d}mu(lfloorfrac dn floor)F(d) end{align*} ]换一个枚举项,枚举(lfloor frac dn floor),设其为(t):
[Ans =sum_{t=1}^{min(lfloor frac ap floor,lfloor frac bd floor)}mu(t)lfloor frac a{td} floorlfloor frac b{td} floor ] -
之后整除分块
-
代码:
#include<bits/stdc++.h> #define debug1 printf("!"); #define debug2 puts("#"); using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn=1e5+5; const int mod=6893911; const int inf=0x3f3f3f3f; template<typename T>inline void read(T&x) { char c; while(!isdigit(c=getchar()));x=c-'0'; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-'0'; } ll smu[maxn+5]; int mu[maxn+5]; int pri[maxn+5],cnt; inline void init() { mu[1]=1; for(int i=2;i<=maxn;i++) { if(!pri[i]) { pri[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&i*pri[j]<=maxn;j++) { pri[i*pri[j]]=1; if(i%pri[j]==0)break; mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=maxn;i++)smu[i]=smu[i-1]+(ll)mu[i]; } inline ll solve(int a,int b,int d) { if(a>b)swap(a,b); ll ans=0; for(int l=1,r;l<=a;l=r+1) { r=min(a/(a/l),b/(b/l)); ans+=1ll*(a/(l*d))*(b/(l*d))*(smu[r]-smu[l-1]); } return ans; } int main() { init(); int T; read(T); while(T--) { int a,b,d; read(a);read(b);read(d); printf("%lld ",solve(a,b,d)); } }
-
题目描述:
设 d(x) 为 x 的约数个数,给定 n,m,求
[sum_{i=1}^nsum_{j=1}^m d(ij) ] -
输入格式:
第一行一个整数T 表述数据组数
接下来T行,每行两个正整数 n,m
-
数据范围:
(1leq T,n,mleq 5 imes 10^4)
-
输出格式:
T行,每行一个整数
指路博客:peng-ym 约数个数和
-
重要性质:
[d(ij)=sum_{x|i}sum_{y|i}[gcd(x,y)=1] ] -
等效(Ans=sum_{i=1}^nsum_{j=1}^msum_{x|i}sum_{y|i}[gcd(x,y)=1])
-
设(f(d))为(gcd(i,j)=d)的个数,(F(n))为 (gcd(i,j)=n)和(n)的倍数 的个数,即:
[f(d)=sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=d] ][F(n)=sum_{n|d} f(d)=lfloorfrac Nn floorlfloorfrac Mn floor ]由莫比乌斯反演:
[f(n)=sum_{n|d}mu(lfloorfrac dn floor)F(d) ] -
于是有:
[egin{align*} Ans &=sum_{i=1}^nsum_{j=1}^msum_{x|i}sum_{y|i}[gcd(x,y)=1]\ &=sum_{i=1}^nsum_{j=1}^msum_{x|i}sum_{y|i}sum_{d|gcd(x,y)}mu(d)\ &=sum_{i=1}^nsum_{j=1}^msum_{x|i}sum_{y|i}sum_{d=1}^{min(n,m)}mu(d)*[d|gcd(x,y)];;;(改枚举d)\ &=sum_{d=1}^{min(n,m)}mu(d)sum_{i=1}^nsum_{j=1}^msum_{x|i}sum_{y|i}[d|gcd(x,y)];;;(将mu(d)提出来)\ end{align*} ]由枚举i,j和它们的约数改变为枚举它们的约数再直接乘上这些约数的倍数的个数。因为每一个约数都会对它的倍数产生贡献:
[egin{align*} Ans&=sum_{d=1}^{min(n,m)}mu(d)sum_{x=1}^nsum_{y=1}^m[d|gcd(x,y)]lfloorfrac nx floorlfloorfrac my floor\ &=sum_{d=1}^{min(n,m)}mu(d)sum_{x=1}^{lfloorfrac nd floor}sum_{y=1}^{lfloorfrac md floor}lfloorfrac n{dx} floorlfloorfrac m{dy} floor;;;(将枚举x,y换成枚举dx,dy)\ &=sum_{d=1}^{min(n,m)}mu(d)(sum_{x=1}^{lfloorfrac nd floor}lfloorfrac n{dx} floor)(sum_{y=1}^{lfloorfrac md floor}lfloorfrac m{dy} floor);;;(lfloorfrac n{dx} floor与y无关,所以可以提前)\ end{align*} ] -
之后整除分块
-
代码:
#include<bits/stdc++.h> #define debug printf("#"); #define pb push_back using namespace std; typedef long long ll; const int mod=998244353; const int maxn=5e4+5; const int inf=0x3f3f3f3f; ll s[maxn]; //ll phi[maxn+5]; int mu[maxn+5]; int pri[maxn],p=0; bool isp[maxn+5]={0}; inline void init() { // phi[1]=1; mu[1]=1; for(int i=2;i<=maxn;i++) { if(!isp[i]) { pri[++p]=i; // phi[i]=i-1; mu[i]=-1; } for(int j=1;j<=p&&i*pri[j]<=maxn;j++) { isp[i*pri[j]]=1; if(i%pri[j]==0) { // phi[i*pri[j]]=phi[i]*pri[j]; break; } // phi[i*pri[j]]=phi[i]*(pri[j]-1); mu[i*pri[j]]=-mu[i]; } } for(int i=2;i<=maxn;i++) { // phi[i]+=phi[i-1]; mu[i]+=mu[i-1]; } for(int i=1;i<=50000;i++) { for(int l=1,r;l<=i;l=r+1) { r=i/(i/l); s[i]+=(r-l+1)*(i/l); } } } int main() { init(); int T; scanf("%d",&T); while(T--) { int n,m,up; ll ans=0; scanf("%d%d",&n,&m); up=min(n,m); for(ll l=1,r;l<=up;l=r+1) { r=min(n/(n/l),m/(m/l)); ans+=1ll*(mu[r]-mu[l-1])*s[n/l]*s[m/l]; } printf("%lld ",ans); } }