题目链接:
BZOJ 2005 能量采集
题意:
一块n*m的土地,能量采集器位与(0, 0),如果一棵植物与能量采集器连接成的线段上有k棵植物,那么能量损失为2*k+1,如果没有植物能量损失为1.求总的能量损失。
分析:
定义f(d)为gcd(x,y)=d(x∈[1,n],y∈[1,n]的(x,y)对数,
则:ans=∑i=ni=1(2∗i−1)∗f(i)
。求解
f(d)
可以利用莫比乌斯反演。
定义F(d)为d | gcd(x,y)的(x,y)的对数,
根据反演规则:F(n)=∑d|ngcd(x,y)→f(n)=∑n|dμ(dn)F(d),
那么ans=∑i=ni=1(2∗i−1)∗∑d=nd=1μ(d)ni∗dni∗d,然后循环跑就行了。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
typedef long long ll;
const int MAX_N = 100010;
bitset<MAX_N> bs;
int prime_cnt;
int prime[MAX_N], mu[MAX_N];
ll sum[MAX_N];
void GetMu()
{
mu[1] = 1;
prime_cnt = 0;
bs.set();
for(int i = 2; i < MAX_N; ++i) {
if(bs[i]) {
prime[prime_cnt++] = i;
mu[i] = -1;
}
for(int j = 0; j < prime_cnt & i * prime[j] < MAX_N; ++j) {
bs[i * prime[j]] = 0;
if(i % prime[j]) {
mu[i * prime[j]] = -mu[i];
}else {
mu[i * prime[j]] = 0;
break;
}
}
}
for(int i = 1; i < MAX_N; ++i ){
sum[i] = sum[i - 1] + mu[i];
}
}
ll solve(int n, int m)
{
ll res = 0;
int top = min(n, m), last;
for(int i = 1; i <= top; i = last + 1) {
last = min(n / (n / i), m / (m / i));
res += (sum[last] - sum[i - 1]) * (n / i) * (m / i);
}
return res;
}
int main()
{
GetMu();
int n, m;
while(~scanf("%d%d", &n, &m)){
ll ans = 0;
int top = min(n, m);
for(int i = 1; i <= top; i++){
ans += solve(n / i, m / i) * (2 * i - 1);
}
printf("%lld\n", ans);
}
return 0;
}