【题解】BZOJ 2005 [Noi2010]能量采集

传送门

Description

根据题目描述以及最基础的找规律(真的很基础),答案就是

i=1nj=1m(2gcd(i,j)1)=2i=1nj=1mgcd(i,j)nm

Solution

其实问题的核心就在于求解

i=1nj=1mgcd(i,j)

我们可以令 f(x) 表示 gcd(i,j)x 的个数。

所以我们有

t=min{n,m}i=1nj=1mgcd(i,j)=k=1tf(k)

当然这个可以用 Mobius 反演在 O(nn) 的时间内得到解决,但这里讲一种 O(nlgn) 的做法。

考虑如何计算 f(x)

找到 1n1m 中所有能整除 x 的数组成的数对, 答案就是

[nx][mx]

但是可以想到这些数对中可能有 gcdx 的倍数的。

举个例子,令 n=7,m=8,x=2 ,满足 x|ix|j 的数对 (i,j) 有:

(2,2),(2,4),(2,6),(2,8)(4,2),(4,4),(4,6),(4,8)(6,2),(6,4),(6,6),(6,8)

12 对,但是可以枚举得到 f(2)=9 ,满足的数对有:
(2,2),(2,4),(2,6),(2,8),(4,2),(4,6),(6,2),(6,4),(6,8)

这是为什么呢?因为 gcd(4,4)=4,gcd(4,8)=4,gcd(6,6)=6 。这些 gcd 值都是 x 的倍数。

而且观察到 f(4)=2,f(6)=1

这样我们就可以这样计算 f(x)

f(x)=[nx][mx]i=2[tx]f(ix)

后面的部分就是将那些 gcdx 的倍数的数都删掉。

写个伪代码吧

Solve(int n,int m)ans0tmin{n,m}for  st  downto  1f[s](n  div  s)×(m  div  s)for  i2  to  t  div  sf[s]f[s]f[i×s]ansans+f[s]×sreturn  ans

Tex 代码如下:

\renewcommand{\tab}[1]{\hskip{#1 em}\hskip{#1 em}} 
\renewcommand{\func}{\text}
\renewcommand{\for}{\textbf{for} ~~}
\renewcommand{\to}{~~ \textbf{to} ~~}
\renewcommand{\downto}{~~ \textbf{downto} ~~}
\renewcommand{\return}{\textbf{return} ~~}
\renewcommand{\div}{~~ \textbf{div} ~~}
\renewcommand{\type}[1]{\textbf {#1} ~}
\begin{aligned}
& \tab{0} \func{Solve}(\type{int} n, \type{int} m) \\
& \tab{1} ans \leftarrow 0 \\
& \tab{1} t \leftarrow \min \{n, m\} \\
& \tab{1} \for s \leftarrow t \downto 1 \\
& \tab{2} f[s] \leftarrow (n \div s) \times (m \div s) \\
& \tab{2} \for i \leftarrow 2 \to  t \div s \\
& \tab{3} f[s] \leftarrow f[s] - f[i \times s] \\
& \tab{2} ans \leftarrow ans + f[s] \times s \\
& \tab{1} \return ans
\end{aligned}

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define min(x, y) ((x) < (y) ? (x) : (y))
long long f[100005];
int main() {
    long long n, m, ans = 0;
    scanf("%lld%lld", &n, &m);
    long long t = min(n, m);
    for (long long s = t; s >= 1; s--) {
        f[s] = (n / s) * (m / s);
        for (long long i = 2; i * s <= t; i++) 
            f[s] -= f[i * s];
    }
    for (long long i = 1; i <= t; i++)
        ans += f[i] * i;
    printf("%lld\n", 2 * ans - n * m);
    return 0;
}
发布了77 篇原创文章 · 获赞 28 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览