Codeforces 871D Paths (欧拉函数 + 结论)

题目链接  Round  #440  Div 1  Problem D

题意   把每个数看成一个点,如果$gcd(x, y) eq 1$,则在$x$和$y$之间连一条长度为$1$的无向边。

     设$d(u, v)$为$u$到$v$之间的最短路,如果$u$和v不连通那么$d(u, v) = 0$

     现在给定$n$,求所有的满足$1 <= u < v <= n$的$d(u, v)$之和。

 

首先把$1$和大于$frac{n}{2}$的质数去掉,这些数和任何数之间的最短距离为$0$。

我们可以得出对于任意$u$, $v$,都有$d(u, v) <= 3$

若$u$和$v$非互素,那么$d(u, v) = 1$;

令$p(x)$为$x$的最小质因子。如果$p(u) cdot p(v) <= n$,那么$d(u, v) = 2$

路径为$u - p(u) cdot p(v) - v$

否则一定存在一条长度为3的路径:$u - 2u - 2v - v$

那么只要求出这三种路径的条数就可以了。

对于长度为$1$的路径,利用欧拉函数可以轻松求出。

对于长度为$2$的路径,设$c[x]$为$p[u] = x$的$u$的个数,$s[]$为$c[]$的前缀和。

那么长度为$2$的路径条数为$∑c_{i} * s_{[frac{n}{i}]}$,注意去掉长度为$1$的情况。

最后长度为$3$的路径条数就是总的合法点对数减去长度为$1$的路径和长度为$2$的路径条数。

时间复杂度$O(nlogn)$

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 1e7 + 10;

int pri[N], p[N], phi[N], c[N], s[N];
int n, m, tot, now;
LL  s1, s2, s3;

int main(){

	scanf("%d", &n);
	phi[1] = 1;
	rep(i, 2, n){
		if (!p[i]){
			p[i] = pri[++tot] = i;
			phi[i] = i - 1;
		
		}
		
		rep(j, 1, tot){
			if (i * pri[j] > n) break;
			p[i * pri[j]] = pri[j];
			if (i % pri[j] == 0){
				phi[i * pri[j]] = phi[i] * pri[j];
				break;
			}
			else phi[i * pri[j]] = phi[i] * (pri[j] - 1);
		}
	}

	rep(i, 2, n) s1 += 0ll + i - 1 - phi[i];
	rep(i, 2, n) ++c[p[i]];
	rep(i, 2, n) s[i] = s[i - 1] + c[i];
	rep(i, 2, n) s2 += 1ll * c[i] * s[n / i];
	rep(i, 2, n) if (1ll * p[i] * p[i] <= n) --s2;

	s2 /= 2;
	s2 -= s1;
	m  = n - 1;
	dec(i, tot, 1){
		if (pri[i] * 2 > n) --m;
		else break;
	}

	s3 = 1ll * m * (m - 1) / 2 - s1 - s2;
	printf("%lld
", s1 + 2 * s2 + 3 * s3);
	return 0;
}

  

原文地址:https://www.cnblogs.com/cxhscst2/p/8620758.html