• YbtOJ选点构形【欧拉函数】


    正题

    题目链接:https://www.ybtoj.com.cn/contest/351/problem/1


    题目大意

    一个圆上,你需要在\(3\sim n\)中选出\(k\)个作为\(a_i\),然后再圆上选择最少的点使得对于每个\(a_i\)你都能用选出的点连成一个正\(a_i\)边形。

    \(k+2\leq n\leq 10^6\)


    解题思路

    首先我们固定一个\(0\)点因为肯定所有的正\(a_i\)边形都交于一个点。

    然后考虑对于一个\(a_i\),我们需要的点就是\(\frac{1}{a_i}\times k(0\leq k<a_i)\)

    注意到一个\(a_i\)如果存在一个\(a_j=k\times a_i\)那么\(a_i\)就不会产生贡献。反过来说\(a_j\)的贡献会减少\(a_i\)

    而我们肯定是优先选择\(a_i\)的,也就是说\(a_j\)选择当且仅当它的所有约数都被选择,而此时\(a_j\)产生的贡献恰好就是\(\varphi(a_j)\)(因为所有\(a_j\)约数产生的贡献和为\(a_j\),我们可以视为它们的贡献都单独为\(\varphi(x)\)

    那么我们可以把\(3\sim n\)按照\(\varphi\)排序从小到大加就好了。

    至于\(1,2\),特判一下比较小的情况就可以了,\(k\)比较大时显然\(1,2\)会被选上去,需要多选\(2\)个。

    时间复杂度:\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e6+10;
    int n,k,cnt,phi[N],pri[N/10];
    bool v[N];
    int main()
    {
    	freopen("point.in","r",stdin); 
    	freopen("point.out","w",stdout);
    	scanf("%d%d",&n,&k);
    	if(k==1)return puts("3")&0;
    	if(k==2)return puts("6")&0;
    	phi[1]=1;
    	for(int i=2;i<=n;i++){
    		if(!v[i])phi[i]=i-1,pri[++cnt]=i;
    		for(int j=1;j<=cnt&&i*pri[j]<=n;j++){
    			v[i*pri[j]]=1;
    			if(i%pri[j]==0){
    				phi[i*pri[j]]=phi[i]*pri[j];
    				break;
    			}
    			phi[i*pri[j]]=phi[i]*phi[pri[j]];
    		}
    	}
    	sort(phi+1,phi+1+n);
    	long long ans=0;
    	for(int i=1;i<=k+2;i++)ans+=phi[i];
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    PHP 上传文件 function move_uploaded_file: failed to open stream
    python super() 方法使用
    python 负数转为无符号整数
    python Aes 加密 解密
    mongoDB 启动 Error: couldn't connect to server 127.0.0.1:27017 src/mongo/shell/mongo.js:91
    ansible使用
    字段类型binary
    7-14 求整数段和
    7-13 日K蜡烛图
    7-12 两个数的简单计算器
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15916119.html
Copyright © 2020-2023  润新知