[NOI2016]循环之美
将题目的本质提出来,就是让我们求:
$$\sum_{i=1}^{m} [gcd(i, k) = 1] \sum_{j=1}^{n} [gcd(i, j) = 1]$$
看到$gcd = 1$这种形式,我们马上就想到了莫比乌斯函数。
通常提到莫比乌斯,我们大多使用它的性质来进行反演,但是我们很容易发现这题无法进行反演(只有一个变量)。
在这里,我们不考虑使用反演的方式,而是从莫比乌斯函数的性质来解决问题。
我们知道莫比乌斯函数有性质:$\sum_{d|n} \mu(d) = [n = 1]$
把这个性质代入到上式中,有:
$$\sum_{i=1}^{m} \sum_{d|i, d|k} \mu(d) \sum_{j=1}^{n} \sum_{t|i, t|j} \mu(t)$$
考虑我们经常使用的更换枚举对象,把枚举约数转化为枚举倍数($j$的意义变为$t$的$j$倍),得到:
$$\sum_{i=1}^{m} \sum_{d|i, d|k} \mu(d) \sum_{t|i}^n \mu(t) \sum_{j=1}^{\frac{n}{t}}$$
即:
$$\sum_{i=1}^{m} \sum_{d|i, d|k} \mu(d) \sum_{t|i}^{n} \mu(t) \frac{n}{t}$$
主要到这里还有$t|i$的限制,于是我们继续把$t$往前提(同样改变了$i$的意义):
$$\sum_{t=1}^{min(n,m)} \mu(t) \frac{n}{t} \sum_{i=1}^{\frac{m}{t}} \sum_{d|it, d|k} \mu(d)$$
现在解决了枚举$t$的问题,又出现了枚举$d$的问题,那就再把$d$往前提一下:
$$\sum_{d|k} \mu(d) \sum_{t = 1}^{min(n,m)} \mu(t) \frac{n}{t} \sum_{i|,d|it}^{\frac{m}{t}}$$
令$w = gcd(t, d)$,则:
$$\sum_{d|k} \mu(d) \sum_{t = 1}^{min(n,m)} \mu(t) \frac{n}{t} \frac{mw}{td}$$
由于$k ≤ 2000$,所以$mu(d) ≠ 0$的$d$最多只有$16$个,线性筛出$\mu$,然后枚举$d$和$t$。
这样复杂度大概是$O(16min(n,m))$,能拿到$72pts$。
继续做下去需要用到杜教筛,就先鸽一下。下面是$72$分的代码:
1 #include2 using namespace std; 3 4 #define int long long 5 const int MAXN = 10000010; 6 7 int cnt, prim[MAXN], mu[MAXN], vis[MAXN]; 8 9 void get_mu(int n) 10 { 11 mu[1] = 1; 12 for(int i = 2; i <= n; ++i) 13 { 14 if(!vis[i]) {prim[++cnt] = i; mu[i] = -1;} 15 for(int j = 1; j <= cnt && i * prim[j] <= n; ++j) 16 { 17 vis[i * prim[j]] = 1; 18 if(i % prim[j] == 0) break; 19 mu[i * prim[j]] = -mu[i]; 20 } 21 } 22 } 23 24 int gcd(int a, int b) 25 { 26 if(!b) return a; 27 return gcd(b, a % b); 28 } 29 30 int n, m, k, ans; 31 32 int solve(int d) 33 { 34 int sum = 0; 35 for(int t = 1; t <= min(n, m); ++t) 36 sum += mu[t] * (n / t) * (m * gcd(t, d) / (t * d)); 37 return sum; 38 } 39 40 signed main() 41 { 42 scanf("%lld %lld %lld", &n, &m, &k); 43 get_mu(max(k, min(n, m))); 44 for(int i = 1; i * i <= k; ++i) 45 if(k % i == 0) 46 { 47 if(mu[i] != 0) ans += mu[i] * solve(i); 48 if(i != k / i && mu[k / i] != 0) ans += mu[k / i] * solve(k / i); 49 } 50 printf("%lld\n", ans); 51 return 0; 52 }