zoukankan      html  css  js  c++  java
  • [SDOI2015]约数个数和(莫比乌斯反演)

    problem

    • \(d(x)\)\(x\) 的约数个数,求:$$ans=\sum_{i=1}{n}\sum_{j=1}{m}d(ij)$$
    • 每个读入文件有 \(T\) 组测试数据,\(T,n,m≤50000\)

    Solution

    • 众所周知
    • \[d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] \]

    • 证明:
    • 考虑每个数对 \((x,y)\) 对应的约数是什么。
    • 因为 \(gcd(x,y)=1\),所以对于任意一个质因子 \(p\),要么 \(x\) 含有 \(p\),要么 \(y\) 含有 \(p\),不可能 \(x,y\) 同时含有 \(p\)
    • 如果包含 \(p\) 的是 \(x\)\(x\)含有 \(a\) 个质因子,那么对应约数中含有 \(a\) 个质因子 \(p\)
    • 否则设 \(i\) 含有 \(b\) 个质因子 \(p\)\(y\) 含有 \(c\) 个质因子 \(p\),那么对应约数中含有 \(b+c\) 个质因子 \(p\)
    • 因此数对 \((x,y)\) 一一对应了 \(ij\) 的每个约数。
    • 那么下面开始推式子:
    • \[ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] \]

    • 我们把 \([gcd(x,y)=1]\) 换成含 \(μ\) 的形式,然后我们枚举 \(x,y\) 分别是 \(d\) 的几倍:
    • \[ans=\sum_{d=1}^{min(n,m)}μ(d)\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{x}\rfloor\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{y}\rfloor \]

    • 显然不同的 \(\lfloor\frac{n}{d}\rfloor\) 只有 \(O(\sqrt n)\) 种(把 \(n\) 换成 \(m\) 也一样)。
    • 那么我们用 \(o(n\sqrt n)\) 的时间预处理出所有的 \(\sum_{x=1}^{n}\lfloor\frac{\lfloor\frac{n}{d}\rfloor}{x}\rfloor\),然后对 \(\lfloor\frac{n}{d}\rfloor\) 整除分块,就可以 \(O(T\sqrt n)\) 回答所有询问。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    
    const int e = 1e5 + 5, lim = 50000;
    int n, m;
    ll ans, f[e], sum[e], miu[e];
    bool b[e];
    
    inline void init()
    {
       int i, j;
       miu[1] = 1;
       for (i = 2; i <= lim; i++) miu[i] = 1;
       for (i = 2; i <= lim; i++)
       if (!b[i])
       {
       		miu[i] = -1;
       		for (j = 2 * i; j <= lim; j += i)
       		{
       			if ((j / i) % i == 0) miu[j] = 0;
       			else miu[j] *= -1;
       			b[j] = 1;
       		}
       }
       for (i = 1; i <= lim; i++)
       for (j = i; j <= lim; j += i)
       f[j]++;
       for (i = 1; i <= lim; i++)
       {
       		sum[i] = sum[i - 1] + miu[i];
       		f[i] += f[i - 1];
       }
    }
    
    int main()
    {
       init();
       int i, tst, j;
       scanf("%d", &tst);
       while (tst--)
       {
       	scanf("%d%d", &n, &m);
       	ans = 0;
       	int tmp = min(n, m);
       	for (i = 1; i <= tmp; i = j + 1)
       	{
       		j = min(n / (n / i), m / (m / i));
       		ans += (sum[j] - sum[i - 1]) * f[n / i] * f[m / i];
       	}
       	printf("%lld\n", ans);
       }
       return 0;
    }
    
  • 相关阅读:
    1.1 java变量及数据类型
    3. 软件测试的类型
    2.3 软件测试模型之 敏捷测试
    2.2 软件测试的手段
    2.1 软件测试的阶段
    1.1 软件测试基础概念
    浅谈内联元素inline
    微信内置浏览器清除缓存的方法
    我的package.json清单
    我的gulp.js清单
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196148.html
Copyright © 2011-2022 走看看