CF1208G Polygons 数论

题目链接https://codeforces.com/contest/1208/problem/G

 
题意:给定两个正整数(n)(k),询问在一个圆上你最少需要几个点,才能在这些点上构造出(k)个边数小于等于(n)的正多边形。

 
分析:我们假设我们选取了一个正(m)边形,那么边数为(m)的因子的所有正多边形也就全部满足了。因此如果我们选了正(m)边形,我们相当于已经选择了所有正(p)边形((p | k))(比如说我们想要选正六边形,那么必须先选择正三角形),增加的点数即为(varphi (m))。因此我们只需要对欧拉函数排序,并将前(k)个累加即可。

upd:官方答案的证明方法可能更好。我们在选正多边形时,可以使每一个正多边形的第一个点重合。那么正(m)边形的每一个点在圆上的位置就可以表示为(0, frac{1}{m} , frac{2}{m} , ... , frac{m-1}{m})。因此在选择了(k)个正多边形后,最简真分数的总数即为我们要求的答案。于是只需要将我们选择的(k)个正多边形的欧拉函数累加即为答案,因为欧拉函数就是互质数数量,也就是新增加的点数。

 
AC代码

#include <bits/stdc++.h>
#define rep(i, a, b) for(long long i = a; i <= b; ++i)
using namespace std;
void io() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
}
long long ans = 2;
const int maxn = 1000000;
bool vis[maxn + 5];
int prime[maxn + 5], phi[maxn + 5], n, k, cnt;
void Phi() {
	phi[1] = 1; vis[1] = true;
	for (int i = 2; i <= maxn; i++) {
		if (!vis[i]) prime[cnt++] = i, phi[i] = i - 1;
		for (int j = 0; j < cnt&&prime[j] * i <= maxn; j++) {
			vis[prime[j] * i] = 1;
			if (i%prime[j] == 0) {
				phi[i*prime[j]] = phi[i] * prime[j];
				break;
			}
			phi[i*prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
}
vector<int> v;
int main() {
	io(); cin >> n >> k;
	if (k == 1) { cout << 3; return 0; }
	Phi();
	rep(i, 3, n) v.emplace_back(phi[i]);
	sort(v.begin(), v.end());
	rep(i, 0, k - 1) ans += v[i];
	cout << ans;
}
原文地址:https://www.cnblogs.com/st1vdy/p/11411997.html