bzoj2818: Gcd 莫比乌斯繁衍

题意:

给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的

数对(x,y)有多少对.

n<=1e7  

题解:两种做法,第一直接推式子。得f[n] = Σd(d是质数)Σd2 (d2 *d <= n)mu(d2)(n/d*d2)^2

           第二是枚举每个素数,然后每个素数p对于答案的贡献就是(1 ~ n / p) 中有序互质对的个数
而求1~m中有序互质对x,y的个数,可以令y >= x, 当y = x时,有且只有y = x = 1互质,当y > x时,确定y以后符合条件的个数x就是phiy
所以有序互质对的个数为(1 ~ n/p)的欧拉函数之和乘2减1(要求的是有序互质对,乘2以后减去(1, 1)多算的一次)
那么就只需要先筛出欧拉函数再求个前缀和就可以了(from hzwer)


关键:题目很简单但是思想很有用。我们可以枚举每个数贡献来解决莫比乌斯繁衍的问题


为什么第一种做法代码更快?。。。QAQ

第一种:

#include
using namespace std;
#define maxn 10000020

typedef long long ll;
bool tag[maxn];
int prime[maxn],cnt,n,mu[maxn];
ll ans;

void init(){
	mu[1] = 1;
	for (register int i = 2 ; i <= n ; i++){
		if ( !tag[i] ) prime[++cnt] = i , mu[i] = -1;
		for (register int j = 1 ; j <= cnt && prime[j] * i <= n ; j++){
			tag[i * prime[j]] = 1;
			if ( (i % prime[j]) == 0 ){ mu[i * prime[j]] = 0; break; }
			mu[i * prime[j]] = mu[i] * (-1);
		}
	}		
	for (register int i = 1 ; i <= n ; i++) mu[i] += mu[i - 1];
}
ll getans(int n){
	register ll cur = 0;
	for (register int i = 1 ; i <= n ; ){
		int next = n / (n / i);
		cur += (ll)(mu[next] - mu[i - 1]) * (n / i) * (n / i);
		i = next + 1;
	}
	return cur;
}
int main(){
	scanf("%d",&n);
	init();
	for (int i = 1 ; i <= cnt ; i++) ans += getans(n / prime[i]);	
	cout<

第二种:

#include
using namespace std;
#define maxn 10000020

typedef long long ll;
bool tag[maxn];
int prime[maxn / 10],cnt,n;
ll phi[maxn];
ll ans;

void init(){
	phi[1] = 1;
	for (register int i = 2 ; i <= n ; i++){
		if ( !tag[i] ) prime[++cnt] = i , phi[i] = i - 1;
		for (register int j = 1 ; j <= cnt && prime[j] * i <= n ; j++){
			tag[i * prime[j]] = 1;
			if ( (i % prime[j]) == 0 ){ phi[i * prime[j]] = phi[i] * prime[j]; break; }
			phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}		
	for (register int i = 1 ; i <= n ; i++) phi[i] += phi[i - 1];	
}

int main(){
	scanf("%d",&n);
	init();
	for (register int i = 1 ; i <= cnt ; i++) ans += phi[n / prime[i]] * 2 - 1;
	cout<

你可能感兴趣的:(ACM,数论)