[NOI2010]能量采集

提供两种做法。

1.简单容斥。

这题乍一看有点像[SDOI2008]仪仗队...

然后我们就想到可能跟gcd有关,然后发现点(i,j)的贡献是gcd(i,j) * 2 - 1

证明如下:

设g = gcd(i,j)

则点(i,j)与原点连线中最靠近原点的点是

因为此点的横纵坐标互质。

那么在连线上的所有点就是p, 2p, 3p...(g - 1)p

共有(g - 1)个点,所以是2g - 1

稍微转化一下,我们枚举g,求有多少对i,j满足gcd(i,j) = g,记为sum[g]

我们发现还是不会......

我们又发现g|gcd(i,j)的数对很好求,是

这样我们只要用这个值减去sum[2g],sum[3g]....即可。

倒序枚举g即可。

long long大法好。

 1 #include <cstdio>
 2 #include <algorithm>
 3 typedef long long LL;
 4 const int N = 100010;
 5 
 6 LL sum[N];
 7 
 8 int main() {
 9     int m, n;
10     LL ans = 0;
11     scanf("%d%d", &n, &m);
12     for(int i = std::min(m, n); i >= 1; i--) {
13         sum[i] = 1ll * (m / i) * (n / i); 
14         for(int j = i << 1; j <= std::min(m, n); j += i) {
15             sum[i] -= sum[j];
16         }
17         //printf("sum[%d] = %lld \n", i, sum[i]);
18         ans += sum[i] * (i * 2 - 1);
19     }
20     printf("%lld", ans);
21     return 0;
22 }
AC代码

 2.莫比乌斯反演。

且 ans = 2 * ∑i * f(i) - (m * n)

运用莫比乌斯反演之后的最终求和式:

 1 #include <cstdio>
 2 typedef long long LL;
 3 const int N = 100010;
 4 
 5 int n, m, c;
 6 int p[N], top, miu[N];
 7 LL f[N];
 8 bool vis[N];
 9 
10 inline void getmiu(int b) {
11     miu[1] = 1;
12     for(int i = 2; i <= b; i++) {
13         if(!vis[i]) {
14             p[++top] = i;
15             miu[i] = -1;
16         }
17         for(int j = 1; j <= top && i * p[j] <= b; j++) {
18             vis[i * p[j]] = 1;
19             if(i % p[j] == 0) {
20                 break;
21             }
22             miu[i * p[j]] = -miu[i];
23         }
24     }
25     return;
26 }
27 
28 inline LL F(int x) {
29     if(f[x]) {
30         return f[x];
31     }
32     return f[x] = 1ll * (m / x) * (n / x);
33 }
34 
35 inline LL solve(int d) {
36     LL ans = 0;
37     for(int i = 1; i * d <= c; i++) {
38         ans += F(i * d) * miu[i];
39     }
40     return ans * d;
41 }
42 
43 int main() {
44     LL ans = 0;
45     scanf("%d%d", &n, &m);
46     c = n > m ? m : n;
47     getmiu(c);
48     for(int i = 1; i <= c; i++) {
49         ans += solve(i);
50     }
51     printf("%lld", 2 * ans - 1ll * m * n);
52     return 0;
53 }
AC代码

 

 

posted @ 2018-08-01 16:16  garage  阅读(118)  评论(0编辑  收藏  举报