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

7 篇文章 0 订阅

传送门

Description D e s c r i p t i o n

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

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

Solution S o l u t i o n

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

i=1nj=1mgcd(i,j) ∑ i = 1 n ∑ j = 1 m gcd ( i , j )

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

所以我们有

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

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

考虑如何计算 f(x) f ( x )

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

[nx][mx]

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

举个例子,令 n=7,m=8,x=2 ,满足 x|i x | i x|j x | j 的数对 (i,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) ( 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 12 对,但是可以枚举得到 f(2)=9 f ( 2 ) = 9 ,满足的数对有:
(2,2),(2,4),(2,6),(2,8),(4,2),(4,6),(6,2),(6,4),(6,8) ( 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 ( 4 , 4 ) = 4 , gcd ( 4 , 8 ) = 4 , gcd ( 6 , 6 ) = 6 。这些 gcd gcd 值都是 x x 的倍数。

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

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

f(x)=[nx][mx]i=2[tx]f(ix) f ( x ) = [ n x ] ⋅ [ m x ] − ∑ i = 2 [ t x ] f ( i ⋅ x )

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

写个伪代码吧

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 C o d e

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值