数论知识总结-莫比乌斯反演

数论知识总结-莫比乌斯反演


NOIP爆零の蒟蒻又来学数论辣

用yyb的博客学了一下莫比乌斯反演


莫比乌斯函数

玄学

\[ \mu(x)=\left\{ \begin{aligned} 1 & (x=1) \\ 0 & (x=p^2\times k,k\in {\mathbb{Z}^+}) \\ (-1)^k & (x=\Pi_{i=1}^{n}{p_i^{k_i}},k_i=1) \\ \end{aligned} \right. \]

x=1时,\(\mu(x)=1\)
x包含质数的平方时,\(\mu(x)=0\)
否则令k=x的质因子个数,\(\mu(x)=(-1)^k\)


莫比乌斯反演

有两种形式??

如果有两个函数\(f(x),g(x)\)

若满足\(g(x)=\sum_{d|x}f(d)\),就能推出\(f(x)=\sum_{d|x}\mu(\frac{x}{d})g(d)\)

若满足\(g(x)=\sum_{x|d,d\le n}f(d)\),就能推出\(f(x)=\sum_{x|d,d\le n}\mu(\frac{d}{x})g(d)\)

如果求不出\(f(x)\),但能快速求出\(g(x)\),任意一个\(f(x)\)就可以快速算出来。

证明不会嗷嗷嗷嗷嗷嗷,能背就好辣


例题

[POI2007]ZAP-Queries

将a/=d,b/=d,问题转化为求gcd(i,j)=1的对数

\(f(x)\)为满足\(gcd(i,j)=x\)的有序数对\((i,j)\)数量,

\(F(x)\)\(gcd(i,j)\)\(x\)倍数的有序数对\((i,j)\)数量。

关系式:\(F(x)=\sum_{x|d,d\le n}f(d)\)

现在要求\(f(1)\)

\(F(x)\)很好求,\(F(x)=\lfloor\frac{a}{x}\rfloor\times\lfloor\frac{b}{x}\rfloor\)

so \(f(x)=\sum_{x|d,d\le n}\mu(\frac{d}{x})F(d)\)

这样枚举可以获得70分的好成绩

好像还有个东西叫数论分块

\(F(x)\)可以分成很多值相等的段,而且最多\(O(\sqrt{n})\)

于是就可以做了

如果求无序数对的话可以写个solve函数,ans=solve(a,b)-solve(min(a,b),min(a,b))/2

// It is made by XZZ
#include
#include
#define il inline
#define rg register
#define vd void
#define sta static
#define int long long
using std::min;
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=50001;
int mu[maxn],Mu[maxn],pr[maxn],g[maxn];
bool flg[maxn];
main(){
    mu[1]=1;
    for(rg int i=2;i

[蛤OI2011]Problem B

和上面一样,简单利用容斥。

// It is made by XZZ
#include
#include
#define il inline
#define rg register
#define vd void
#define sta static
using namespace std;
typedef long long ll;
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=50001;
int mu[maxn],Mu[maxn],pr[maxn];
bool yes[maxn];
il ll solve(int x,int y,int k){
    if(x==0||y==0)return 0;
    if(x>y)swap(x,y);
    x/=k,y/=k;
    int i=1,j;
    ll ret=0;
    while(i<=x){
        j=min(x/(x/i),y/(y/i));
        ret+=1ll*(Mu[j]-Mu[i-1])*(x/j)*(y/j);
        i=j+1;
    }
    return ret;
}
main(){
    mu[1]=1;
    for(rg int i=2;i

长期更新

转载于:https://www.cnblogs.com/xzz_233/p/8360903.html

你可能感兴趣的:(数论知识总结-莫比乌斯反演)