[cf 1208G] Polygons

题意

在圆周上画出\(k\)个内接正多边形,要求\(k\)个正多边形边数不同,且最大的边数不超过\(n\),使得总顶点数最小。

题解

神仙题反映了我不会数学的事实。
考虑如果选了一个正\(n\)边形,那么必定会选择满足\(m | n\)的正\(m\)边形。
在考虑到最优的方案中,一定会让所有多边形共同一个点。
考虑这个点为\(O\),设圆周长为1,则正\(n\)边形上的点到点\(O\)的圆上距离为

\[\frac{0}{n}, \frac{1}{n}, \frac{2}{n}, \ldots, \frac{n - 1}{n} \]

考虑因为选了正\(n\)边形时,一定会选择满足\(m | n\)的正\(m\)边形。
这相当于正\(n\)边形独自产生的贡献是分母为\(n\)的最简真分数的个数(除去\(\frac{0}{n}\))。
这样,为了选出\(k\)个正多边形,就要让选出的正多边形独自产生的贡献和最小。
这样只要排个序取前\(k\)小求和即可。
需要注意的是,由于没有“一边形”和“二边形”,但是它们依然符合我们上面的类比分析。
所以就可以令“一边形”的贡献为1(代表\(O\),即\(\frac{0}{n}\)),“二边形”的贡献为1(即\(\frac{1}{2}\))。
\(k > 1\)时,它们的贡献要囊括进来(在\(k = 1\)时,“二边形”不会产生贡献),但是由于没有“一边形”,“二边形”这种东西,所以不能算进\(k\)个正多边形里面。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int n, k; long long ans;
int p[N], phi[N]; bool vis[N];
vector <int> coe;
void sieve () {
	for (int i = 2; i <= n; ++i) {
		if (!vis[i]) {
			p[++p[0]] = i;
			phi[i] = i - 1;
		}
		for (int j = 1; j <= p[0] && i * p[j] <= n; ++j) {
			vis[i * p[j]] = 1;
			if (i % p[j] == 0) {
				phi[i * p[j]] = phi[i] * p[j];
				break;
			}
			phi[i * p[j]] = phi[i] * (p[j] - 1);
		}
	}
}

int main () {
	cin >> n >> k, sieve();
	if (k == 1) {
		return puts("3"), 0;
	}
	for (int i = 3; i <= n; ++i) {
		coe.push_back(phi[i]);
	}
	sort(coe.begin(), coe.end());
	for (int i = 0; i < k; ++i) {
		ans += coe[i];
	}
	cout << ans + 2 << endl;
	return 0;
}
posted @ 2019-10-01 14:38  psimonw  阅读(203)  评论(0编辑  收藏  举报