[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;
}