码迷,mamicode.com
首页 > 其他好文 > 详细

luogu3327 [SDOI2015]约数个数和

时间:2019-01-21 16:12:44      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:cti   false   name   scanf   前置   分块   mes   [1]   cst   

link

\(d(x)\)表示x约数个数,给定n,m,\(\sum_{i=1}^n\sum_{j=1}^md(ij)\)

多组询问,1<=T<=50000,1<=N, M<=50000

前置知识:\(d(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]\)

证明:\(xy\)一定是\(ij\)的约数。考虑一个质数\(p\)\(i\)中包含\(p^a\)\(j\)中包含\(p^b\),则\(ij\)中包含的是\(p^{a+b}\)。若\(\gcd(x,y)=1\),说明\(x,y\)中至少有一个数的\(p^k\)为1,容斥一下就有\(a+b+1\)种,而\(p^{a+b}\)中也恰好有\(a+b+1\)的贡献。最后把所有质数贡献乘起来就是答案。

推式子即可:

\(\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]\)

\(=\sum_{x=1}^n\sum_{y=1}^m\lfloor\frac nx\rfloor\lfloor\frac my\rfloor[\gcd(x,y)=1]\)

\(=\sum_{x=1}^n\sum_{y=1}^m\lfloor\frac nx\rfloor\lfloor\frac my\rfloor\sum_{d|x,d|y}\mu(d)\)

\(=\sum_{d=1}^n\mu(d)\sum_{x=1}^{n/d}\sum_{y=1}^{m/d}\lfloor\frac n{xd}\rfloor\lfloor\frac m{yd}\rfloor\)

\(f(n)=\sum_{i=1}^n\lfloor\frac ni\rfloor=\sum_{i=1}^nd(i)\),这里的\(d\)是约数个数,由于\(n\le50000\),这个可以预处理线性筛约数个数

则原式=\(\sum_{d=1}^n\mu(d)f(\lfloor\frac nd\rfloor)f(\lfloor\frac md\rfloor)\),直接上数论分块


#include <cstdio>
#include <functional>
using namespace std;

int prime[50010], fuck = 50000, tot, d[50010], d1[50010], mu[50010];
bool vis[50010];

int main()
{
    mu[1] = d[1] = d1[1] = 1;
    for (int i = 2; i <= fuck; i++)
    {
        if (vis[i] == false) { prime[++tot] = i, mu[i] = -1, d[i] = d1[i] = 2; }
        for (int j = 1; j <= tot && i * prime[j] <= fuck; j++)
        {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                d1[i * prime[j]] = d1[i] + 1;
                d[i * prime[j]] = d[i] / d1[i] * d1[i * prime[j]];
                break;
            }
            d1[i * prime[j]] = 2;
            d[i * prime[j]] = d[i] * 2;
            mu[i * prime[j]] = -mu[i];
        }
        d[i] += d[i - 1];
        mu[i] += mu[i - 1];
    }
    int t; scanf("%d", &t);
    while (t --> 0)
    {
        int n, m;
        long long ans = 0;
        scanf("%d%d", &n, &m);
        if (n > m) swap(n, m);
        for (int i = 1, j; i <= n; i = j + 1)
        {
            j = min(n / (n / i), m / (m / i));
            ans += (mu[j] - mu[i - 1]) * (long long)d[n / i] * d[m / i];
        }
        printf("%lld\n", ans);
    }
    return 0;
}

45行一遍AC

最近做数论题都不用观察题解了。。。

luogu3327 [SDOI2015]约数个数和

标签:cti   false   name   scanf   前置   分块   mes   [1]   cst   

原文地址:https://www.cnblogs.com/oier/p/10298932.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!