BZOJ 2005 能量采集（莫比乌斯反演）

BZOJ 2005 能量采集

f(d)gcd(x,y)=d(x[1,n],y[1,n](x,y)$定义f(d) 为gcd(x, y)=d(x \in [1, n],y \in [1,n]的(x,y)对数，$
ans=i=ni=1(2i1)f(i)$则：ans = \sum_{i = 1}^{i = n}(2*i - 1) *f(i)$。求解f(d)$f(d)$可以利用莫比乌斯反演。F(d)d | gcd(x,y)(x,y)$定义F(d)为d\ |\ gcd(x,y)的(x,y)的对数，$
:F(n)=d|ngcd(x,y)f(n)=n|dμ(dn)F(d),$根据反演规则:F(n)= \sum_{d|n}gcd(x,y) \rightarrow f(n) = \sum_{n|d}{} \mu(\frac{d}{n})F(d),$
ans=i=ni=1(2i1)d=nd=1μ(d)nidnid,$那么ans =\sum_{i = 1}^{i = n}(2*i-1)*\sum_{d=1}^{d=n}\mu(d)\frac{n}{i*d}\frac{n}{i*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;
}

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客