zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Nowcoder9981A.串(排列组合)
    267D.Fedor and Essay(强连通分量缩点+DAG上DP)
    1290C. Prefix Enlightenment(带权并查集)
    LeetCode1. 两数之和
    LeetCode451. 根据字符出现频率排序
    LeetCode205. 同构字符串
    LeetCode290. 单词规律
    LeetCode202. 快乐数
    LeetCode242. 有效的字母异位词
    LeetCode136. 只出现一次的数字
  • 原文地址:https://www.cnblogs.com/st1vdy/p/11411997.html
Copyright © 2011-2022 走看看