2019.01.19【NOI2010】【BZOJ2005】【洛谷1447】能量采集(莫比乌斯反演)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/86554604

BZOJ传送门

洛谷传送门


解析:

当然这道题有常数十分优秀的容斥 O ( n log n ) O(n\log n) 做法:https://blog.csdn.net/zxyoi_dreamer/article/details/82289575

以及复杂度十分优秀的 O ( n ) O(n) ~ O ( n ) O(\sqrt n) 做法。
(这个符号就是指 O ( n ) O(n) )预处理, O ( n ) O(\sqrt n) 回答每个询问

思路:

O ( n ) O(n) 做法就是莫比乌斯反演,不过因为线性筛的较大常数被容斥做法吊打。

要不开一下 1 e 7 1e7 ?

首先将所有格子上的数+1可以发现格子上的数就是行和列的 g c d × 2 gcd\times 2

那么问题就是这个了: i = 1 n j = 1 m g c d ( i , j ) \sum_{i=1}^n\sum_{j=1}^mgcd(i,j)

莫比乌斯反演,首先大力推式子:

d d i = 1 n j = 1 m [ g c d ( i , j ) = d ] = d d i = 1 n d j = 1 m d [ g c d ( i , j ) = 1 ] = d d ( T = 1 min ( n , m ) d n d T m d T μ ( T ) ) \begin{aligned} &\sum_{d}d\sum_{i=1}^{n}\sum_{j=1}^m[gcd(i,j)=d]\\ =&\sum_{d}d\sum_{i=1}^{\lfloor\frac{n}d\rfloor}\sum_{j=1}^{\lfloor\frac{m}d\rfloor}[gcd(i,j)=1]\\ =&\sum_{d}d(\sum_{T=1}^{\lfloor\frac{\min(n,m)}{d}\rfloor}\lfloor\frac{n}{dT}\rfloor\lfloor\frac{m}{dT}\rfloor\mu(T)) \end{aligned}

可以 O ( n n ) O(n\sqrt n) 回答每个询问了,AC此题已经没问题了。

但是我们的目标是 O ( n ) O(\sqrt n) 回答每个询问,继续化简:

考虑改变枚举顺序,这次变动有点大,请读者仔细观察

A n s = t = 1 min ( n , m ) n t m t D t μ ( D ) t D \begin{aligned} Ans=\sum_{t=1}^{\min(n,m)}\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}t\rfloor\sum_{D\mid t}\mu(D)\frac{t}D \end{aligned}

然后后面这个东西: D t μ ( D ) t D \sum_{D\mid t}\mu(D)\frac{t}{D} ,学过 D i r i c h l e t Dirichlet 卷积的都知道, μ I d = ϕ \mu*Id=\phi ,所以这个就是个欧拉函数。

所以我们要求的东西 i = 1 n j = 1 m g c d ( i , j ) = t = 1 min ( n , m ) n t m t ϕ ( t ) \sum_{i=1}^n\sum_{j=1}^mgcd(i,j)=\sum_{t=1}^{\min(n,m)}\lfloor\frac{n}t\rfloor\lfloor\frac{m}t\rfloor\phi(t)

于是就可以整除分块了,维护一下 ϕ \phi 的前缀和就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define cs const

cs int P=100005;
int prime[P],pcnt,phi[P];
bool mark[P];

inline void linear_sieves(int len=P-5){
	phi[1]=1;
	for(int re i=2;i<=len;++i){
		if(!mark[i])prime[++pcnt]=i,phi[i]=i-1;
		for(int re j=1;i*prime[j]<=len;++j){
			mark[i*prime[j]]=true;
			if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

int n,m;
ll ans;
signed main(){
	linear_sieves();
	scanf("%d%d",&n,&m);
	for(int re i=1;i<=min(n,m);++i)ans+=(ll)(n/i)*(m/i)*phi[i];
	cout<<2*ans-(ll)m*n; 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/86554604