[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;
}
上一篇:python – 在matplotlib中的散点图中的数据点周围绘制一个光滑的多边形


下一篇:javascript-如何“排序”多边形3d?