zoukankan      html  css  js  c++  java
  • 莫比乌斯反演专题

    前段时间教练讲了莫比乌斯反演,决定发一篇博客来加深自己的理解。(众所周知我是个很懒的人,不愿意去打非常复杂的(latex),所以我的题解里很少有需要很复杂的推式子的题。。。)

    【hdu1695】GCD

    题面

    hdu

    题解

    题意:求

    [sum_{i=1}^a sum_{j=1}^b [gcd(i,j)==d] ]

    不难发现和这个式子是等价的

    [sum_{i=1}^{a/d} sum_{j=1}^{b/d} [gcd(i,j)==1] ]

    为了方便之后的表达,后面的(a)(b)分别为(a/d)(b/d)
    我们设

    [f(d)=sum_{i=1}^{a} sum_{j=1}^{b} [gcd(i,j) == d] ]

    [F(n) = sum_{n|d}f(d) ]

    此时

    [F(x)=lfloor frac{a}{x} floor lfloor frac{b}{x} floor ]

    根据莫比乌斯反演公式

    [F(n)=sum_{n|d}f(d) ]

    [f(n)=sum_{n|d} mu(frac{d}{n})F(d) ]

    那么我们的答案其实就是(f(1))
    这里还有(1)个问题。
    那就是(gcd(x,y))(gcd(y,x))是一样的。
    那我们要将总答案减去这种情况(/2)
    (当时写这份代码的时候我还不会整除分块,所以代码里并没有...)

    代码

    #include <bits/stdc++.h>
      
    const int maxn = 1e5 + 10;
    typedef long long ll;  
      
    bool vis[maxn];       
    int pri[maxn], mu[maxn];   
    int tot, n, m, i, j, k, T, a, b, ctot;    
      
    inline void prep(int n) {
        mu[1] = 1;    
        for(int i = 2;i <= n;i++) {
            if(!vis[i]) { pri[ ++pri[0] ] = i;  mu[i] = -1; }
            for(int j = 1;j <= pri[0] && i * pri[j] <= n;j++) {
                vis[ i * pri[j] ] = 1;    
                if(i % pri[j]) 
                    mu[ i * pri[j] ] = -mu[i];
                else {
                    mu[ i * pri[j] ] = 0;
                    break;    
                }
            }
        }
    }
      
    int main() {
        prep(maxn - 10); 
        scanf("%d",&T);
        while(T--) {
            scanf("%d %d %d",&a,&b,&k);  
            printf("Case %d: ",++ctot);         
            if(!k) { puts("0");  continue; }
            a /= k;  b /= k;       
            if(a > b)
                a = b;       
            ll r1 = 0, r2 = 0;
            for(int i = 1;i <= std::min(a,b);i++)
                r1 += 1ll * mu[i] * (a / i) * (b / i); 
            for(int i = 1;i <= std::min(a,b);i++)
                r2 += 1ll * mu[i] * (std::min(a,b) / i) * (std::min(a,b) / i);   
            printf("%lld
    ",r1 - r2 / 2);   
        }
        return 0;
    }
    

    [HAOI2011] Problem b

    题面

    洛谷

    题解

    好像就是上面那道题加个差分...
    具体看代码吧。

    代码

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
     
    const int maxn = 1e5 + 10;
    typedef long long ll;  
     
    bool vis[maxn];       
    int pri[maxn], mu[maxn], s[maxn];   
    int tot, n, m, i, j, k, T, a, b, ctot, c, d;    
     
    inline void prep(int n) {
        mu[1] = 1;    
        for(int i = 2;i <= n;i++) {
            if(!vis[i]) { pri[ ++pri[0] ] = i;  mu[i] = -1; }
            for(int j = 1;j <= pri[0] && i * pri[j] <= n;j++) {
                vis[ i * pri[j] ] = 1;    
                if(i % pri[j]) 
                    mu[ i * pri[j] ] = -mu[i];
                else {
                    mu[ i * pri[j] ] = 0;
                    break;    
                }
            }
        }
        for(int i = 1;i <= n;i++)
        	s[i] = s[i - 1] + mu[i];  
    }
     
    inline ll get(int a,int b) {
    	int c = std::min(a,b);  int res = 0;
    	for(int l = 1, r;l <= c;l = r + 1) {
    		r = std::min(a / (a / l),b / (b / l)); 
    		res += (1ll * a / (1ll * l * k)) * (1ll * b / (1ll * r * k)) * (s[r] - s[l - 1]);      
    	}
    	return res;
    } 
     
    int main() {
        prep(maxn - 10); 
        scanf("%d",&T);
        while(T--) {
    		scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
    		printf("%lld
    ",get(b,d) - get(b,c - 1) - get(a - 1,d) + get(a - 1,c - 1));
        }
    	return 0;    
    }
    

    YY的GCD

    题面

    洛谷

    题解

    题意:

    [sum_{i=1}^n sum_{j=1}^m sum_{k} [gcd(i,j)==k &&k in prime ] ]

    第一道题设同样的东西:

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

    [F(n) = sum_{n|d}f(d) ]

    [F(d) = lfloor frac{n}{d} floor lfloor frac{m}{d} floor ]

    此时

    [f(n)=sum_{n|d} mu(lfloor frac{d}{n} floor) F(d) ]

    开始解这道题,求的就是

    [sum_{p in prime}f(p) ]

    代入公式

    [sum_{p in prime} sum_{p|d} mu(lfloor frac{d}{p} floor) F(d) ]

    枚举(lfloor frac{d}{p} floor)

    [sum_{p in prime} sum_{d=1}^{min(lfloor frac{n}{p} floor, lfloor frac{m}{p} floor)}mu(d)F(dp) ]

    [sum_{p in prime} sum_{d=1}^{min(lfloor frac{n}{p} floor, lfloor frac{m}{p} floor)}mu(d) lfloor frac{n}{dp} floor lfloor frac{m}{dp} floor ]

    (dp)(T),然后枚举(T)

    [sum_{T=1}^{min(n,m)} sum_{t|T,t in prime} mu (lfloor frac{T}{t} floor) lfloor frac{n}{T} floor lfloor frac{m}{T} floor ]

    [sum_{T=1}^{min(n,m)} lfloor frac{n}{T} floor lfloor frac{m}{T} floor (sum_{t|T} mu(lfloor frac{T}{t} floor)) ]

    然后就可以做了。

    代码

    #include <bits/stdc++.h>
    
    const int maxn = 1e7 + 10;
    typedef long long ll;  
    
    inline void _swap(int& a,int& b) {
    	a ^= b ^= a ^= b;  
    }
    
    int n, m, i, j, k, T;
    int mu[maxn], f[maxn], s[maxn], pri[maxn];   
    bool vis[maxn]; 
    
    inline void sieve(int n) {
    	mu[1] = 1;
    	for(int i = 2;i <= n;i++) {
    		if(!vis[i]) { pri[ ++pri[0] ] = i;  mu[i] = -1; }
    		for(int j = 1;j <= pri[0] && i * pri[j] <= n;j++) {
    			vis[ i * pri[j] ] = 1;  
    			if(i % pri[j])
    				mu[ i * pri[j] ] = -mu[i];
    			else {
    				mu[ i * pri[j] ] = 0;
    				break;
    			}
    		}
    	}
    	for(int i = 1;i <= pri[0];i++)
    		for(int j = 1;j * pri[i] <= n;j++)
    			f[ j * pri[i] ] += mu[j];
    	for(int i = 1;i <= n;i++)
    		s[i] = s[i - 1] + f[i];   
    }
    
    int main() {
    	sieve(maxn - 10);
    	scanf("%d",&T);
    	while(T--) {
    		scanf("%d %d",&n,&m);
    		if(n > m)
    			_swap(n,m);	
    		ll ans = 0;  
    		for(int l = 1, r = 0;l <= n;l = r + 1) {
    			r = std::min(n / (n / l),m / (m / l));
    			ans += 1ll * (s[r] - s[l - 1]) * (ll)(n / l) * (ll)(m / l);  
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;    
    }
    
    
  • 相关阅读:
    各系统添加根证书
    Nginx浏览目录配置及美化
    django-图形验证码(django-simple-captcha)
    django-manage.py参数
    js — 对象
    js — 数组Array
    js — 字符串
    js — 基础知识
    css — 定位、背景图、水平垂直居中
    css — 权重、继承性、排版、float
  • 原文地址:https://www.cnblogs.com/Sai0511/p/11314769.html
Copyright © 2011-2022 走看看