CF1208G Polygons 数论

时间:2019-08-26
本文章向大家介绍CF1208G Polygons 数论,主要包括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