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

传送门

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) t = min { n , m } ∑ i = 1 n ∑ j = 1 m gcd ( i , j ) = ∑ k = 1 t f ( 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] [ n x ] ⋅ [ m x ]

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

举个例子,令 n=7,m=8,x=2 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 ( 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 Solve ( int   n , int   m ) a n s ← 0 t ← min { n , m } for     s ← t     downto     1 f [ s ] ← ( n     div     s ) × ( m     div     s ) for     i ← 2     to     t     div     s f [ s ] ← f [ s ] − f [ i × s ] a n s ← a n s + f [ s ] × s return     a n s

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
#include
#include
#include
#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;
}

你可能感兴趣的:(BZOJ,数论)