BZOJ2671 Calc [莫比乌斯反演]

传送门

设 d = gcd(a, b), a = ud, b = vd

a+b|ab\Leftrightarrow d(u + v)|d^2uv\rightarrow u + v|duv

因为 gcd(u, v) = 1, 所以 gcd(u+v, v) = gcd(u+v, u) = 1

u + v|d

ans=\sum_d\sum_u\sum_{v<u} [u + v|d,ud<=n][(u,v)=1]

=\sum_u\sum_{v<u} \left \lfloor \frac{n}{u(u+v)}\right \rfloor[(u,v)=1]

=\sum_{d=1}^n \mu(d) \sum_{u}\sum_{v<u}\left \lfloor \frac{n}{u(u+v)d^2}\right \rfloor

然后卡一下上界

=\sum_{d=1}^{\sqrt n} \mu(d) \sum_{u}^{\sqrt{n/d^2}}\sum_{k= u+1}^{ukd^2<=n}\left \lfloor \frac{n}{ukd^2}\right \rfloor

枚举 k 的时候再整除分块就可以直接怼过去


#include<bits/stdc++.h>
#define N 100050
using namespace std;
typedef long long ll;
ll n; int prim[N], isp[N], mu[N], tot;
void prework(int m){
	mu[1] = 1;
	for(int i = 2; i <= m; i++){
		if(!isp[i]) prim[++tot] = i, mu[i] = -1;
		for(int j = 1; j <= tot; j++){
			if(i * prim[j] > m) break;
			isp[i * prim[j]] = 1;
			if(i % prim[j] == 0){ mu[i * prim[j]] = 0; break;}
			mu[i * prim[j]] = mu[i] * mu[prim[j]];
		}
	}
}
ll calc(ll n, ll m){
	ll sum = 0;
	for(int u = 1; u <= m; u++){
		int t = n / u, up = (u << 1) - 1;
		for(int l = u + 1, r = 0; l <= t, l <= up; l = r + 1){
			int v = t / l; if(!v) break;
			r = min(t / v, up); sum += 1ll * (r - l + 1) * v; 
		}
	} return sum;
}
int main(){
	scanf("%d", &n); int m = sqrt(n); prework(m);
	ll ans = 0; 
	for(ll i = 1; i <= m; i++) if(mu[i]) ans += mu[i] * calc(n / i / i, m / i);
	cout << ans; return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值