HDU 1695 莫比乌斯反演

    xiaoxiao2021-03-25  117

    莫比乌斯反演

    对于定义在非负整数上的两个函数F(x), f(x) : 若 F(n)=d|nf(d)

    f(n)=d|nu(d)F(nd) (1) 其中: u(d) 就是莫比乌斯函数, 它的定义如下 u(d)=1,(1)k, 0,d=1d=p1p2p3pk,piiϵ[1,k](2)

    常见性质 :

    d|nu(d)={1, 0,n=1n>(3) 证明 : (1) d|nu(d)F(nd)=d|nu(d)d|ndf(d) =d|ndu(d)d|nf(d)(3) =d|1u(d)f(n)=f(n)

    还有一种形式的反演变换: 若 F(n)=n|df(d)  

    f(n)=n|du(dn)F(d)(1)

    典型的反演: n=d|nϕ(d) ϕ(n)=d|nu(d)nd

    /*莫比乌斯函数线性筛法*/ int mobius[N], prime[N]; bool vis[N]; void getMobius(){ memset(vis, 0, sizeof vis); memset(mobius, 0, sizeof mobius); mobius[1] = 1; int cnt = 0; for(int i = 2; i < N; ++i){ if(!vis[i]) { mobius[i] = -1;/*质数**/ prime[++cnt] = i; } for(int j = 1; j <= cnt && i * prime[j] < N; ++j){ vis[prime[j] * i] = 1; if(i % prime[j] == 0) { mobius[i * prime[j]] = 0; /**某个质因子出现两个以上*/ break; } mobius[i * prime[j]] = mobius[i]; } } }

    题解

    分别在[1, n]和[1, m]中找出一对x, y满足gcd(x, y) = k 就是求在[1, n / k]和[1,m / k]中找出一对x’, y’满足gcd(x’, y’) = 1 暴力枚举极端情况O(n^2)n最大100000会T 所以定义 f(d)[1,N][1,M]gcd(x,y)=d 在定义 F(x)[1,N][1,M]gcd(x,y)d

    明显 F(x)=N/xM/x 且有公式 F(x)=x|df(d)

    根据莫比乌斯反演, 得: f(x)=x|du(dx)F(d) 因此

    f(1)=i=1min(N,M)u(i)F(i) 然后在去掉重复的对数:[1, min(N, M)]有一半重复就可解

    code

    #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int N = 100005; int mobius[N], prime[N]; bool vis[N]; int a, b, c, d, k; void init(){ memset(vis, 0, sizeof vis); memset(mobius, 0, sizeof mobius); mobius[1] = 1; int cnt = 0; for(int i = 2; i < N; ++i){ if(!vis[i]) { mobius[i] = -1; prime[++cnt] = i; } for(int j = 1; j <= cnt && i * prime[j] < N; ++j){ vis[prime[j] * i] = 1; if(i % prime[j] == 0) { mobius[i * prime[j]] = 0; break; } mobius[i * prime[j]] = -mobius[i]; } } } inline void input(){ cin >> a >> b >> c >> d >> k; } void solve(){ if(b < d) swap(b, d); b /= k; d /= k; ll ans1 = 0, ans2 = 0; for(int i = 1; i <= d; ++i){ ans1 += mobius[i] * 1ll * (b / i) * (d / i); ans2 += mobius[i] * 1ll * (d / i) * (d / i); } cout << ans1 - (ans2 >> 1) << endl; } int main(){ int cas = 1, t; cin >> t; init(); while(t--){ input(); cout << "Case " << cas++ << ": "; if(k == 0) { cout << "0" << endl;continue; } solve(); } return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-21450.html

    最新回复(0)