zoukankan      html  css  js  c++  java
  • @51nod


    @description@

    定义F(n)表示最小公倍数为n的二元组的数量。
    即:如果存在两个数(二元组)X,Y(X <= Y),它们的最小公倍数为N,则F(n)的计数加1。
    例如:F(6) = 5,因为[2,3] [1,6] [2,6] [3,6] [6,6]的最小公倍数等于6。
    给出一个区间[a,b],求最小公倍数在这个区间的不同二元组的数量。
    例如:a = 4,b = 6。符合条件的二元组包括:
    [1,4] [2,4] [4,4] [1,5] [5,5] [2,3] [1,6] [2,6] [3,6] [6,6],共10组不同的组合。

    原题传送门。

    @solution@

    将 [X, Y] 变为 [1, Y] - [1, X-1],将无序对转有序对,处理一下相等的情况。

    那么就是求 (sum_{i=1}^{n}sum_{j=1}^{n} [frac{i imes j}{gcd(i, j)} leq n])。套路反演一波得到:

    [sum_{d=1}^{n}sum_{i=1}^{n}sum_{j=1}^{n}[gcd(i, j) = d][frac{i imes j}{gcd(i, j)} leq n] \ sum_{d=1}^{n}sum_{i=1}^{n}sum_{j=1}^{n}[gcd(i, j) = 1][i imes j imes d leq n] \ sum_{d=1}^{n}sum_{i=1}^{n}sum_{j=1}^{n}sum_{p=1}^{n}mu(p)[i imes j imes d imes p^2 leq n] \ sum_{p=1}^{sqrt{n}}mu(p)sum_{d=1}^{n}sum_{i=1}^{n}sum_{j=1}^{n}[i imes j imes d leq lfloorfrac{n}{p^2} floor] ]

    记 f(m) 表示 abc <= m 对应的有序对 (a, b, c) 数量。可以用杜教筛等方法做(不过貌似过不了),但是还有一个更有趣的做法:

    我们不妨假设 a <= b <= c,然后根据里面 a, b, c 互相之间的相等关系算这样的无序对的贡献。
    那么显然有 (a leq m^{frac{1}{3}}, b leq sqrt{frac{m}{a}})。然后暴力在范围内枚举 a, b 即可。

    算一个 f(m) 时间复杂度为 (O(sum_{i=1}^{m^{frac{1}{3}}}sqrt{frac{m}{i}})),简单积分拟合一下可以算出大概是 (O(n^{frac{2}{3}}))

    然后外层枚举莫比乌斯函数,总时间复杂度为 (O(sum_{i=1}^{sqrt{n}}(frac{n}{i^2})^{frac{2}{3}}))。这玩意儿不定积分原函数在 x = 0 没有定义,不过我们可以直接代 x = 1(因为是从 1 ~ (sqrt{n})),结果算出来还是 (O(n^{frac{2}{3}}))

    @accepted code@

    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 1000000;
    
    bool nprm[MAXN + 5];
    int prm[MAXN + 5], mu[MAXN + 5], pcnt;
    void init() {
    	mu[1] = 1;
    	for(int i=2;i<=MAXN;i++) {
    		if( !nprm[i] ) prm[++pcnt] = i, mu[i] = -1;
    		for(int j=1;i*prm[j]<=MAXN;j++) {
    			nprm[i*prm[j]] = true;
    			if( i % prm[j] == 0 ) {
    				mu[i*prm[j]] = 0;
    				break;
    			}
    			else mu[i*prm[j]] = -mu[i];
    		}
    	}
    }
    
    ll get(ll m) {
    	ll ans = 0;
    	for(ll i=1;i*i*i<=m;i++) {
    		for(ll j=i;i*j*j<=m;j++) {
    			ll lb = j, ub = m / i / j;
    			ans += (j == i ? 3 : 6)*max(0LL, ub - lb);
    			ans += 3*(j != i);
    		}
    		ans++;
    	}
    	return ans;
    }
    
    ll solve(ll n) {
    	ll ans = 0;
    	for(ll i=1;i*i<=n;i++)
    		ans = ans + mu[i] * get(n / i / i);
    	return (ans + n) / 2;
    }
    
    ll gcd(ll a, ll b) {
    	return b == 0 ? a : gcd(b, a % b);
    }
    
    ll d[MAXN + 5];
    int main() {
    	init();
    	ll a, b; scanf("%lld%lld", &a, &b);
    	printf("%lld
    ", solve(b) - solve(a - 1));
    }
    

    @details@

    莫比乌斯反演的应用分析(×)
    积分在时间复杂度分析中应用(√)

    不要想着 cmath 库里面的 pow 函数的精度,直接把 (a leq n^{frac{1}{3}}) 转变成 (a^3 leq n)

  • 相关阅读:
    AIX 日常维护
    复制百度文库的方法
    linux free命令详解
    Oracle中Kill session的研究
    oracle scn的研究转自听海日志
    Win7无法待机(休眠)的一种可能原因
    vmstat命令详解
    winform与数据库同步更新
    CAD—Keyboard事件处理
    游戏之寻路算法
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12462387.html
Copyright © 2011-2022 走看看