正题
题目链接:https://www.ybtoj.com.cn/contest/351/problem/1
题目大意
一个圆上,你需要在3∼n3\sim n3∼n中选出kkk个作为aia_iai,然后再圆上选择最少的点使得对于每个aia_iai你都能用选出的点连成一个正aia_iai边形。
k+2≤n≤106k+2\leq n\leq 10^6k+2≤n≤106
解题思路
首先我们固定一个000点因为肯定所有的正aia_iai边形都交于一个点。
然后考虑对于一个aia_iai,我们需要的点就是1ai×k(0≤k<ai)\frac{1}{a_i}\times k(0\leq k<a_i)ai1×k(0≤k<ai)。
注意到一个aia_iai如果存在一个aj=k×aia_j=k\times a_iaj=k×ai那么aia_iai就不会产生贡献。反过来说aja_jaj的贡献会减少aia_iai。
而我们肯定是优先选择aia_iai的,也就是说aja_jaj选择当且仅当它的所有约数都被选择,而此时aja_jaj产生的贡献恰好就是φ(aj)\varphi(a_j)φ(aj)(因为所有aja_jaj约数产生的贡献和为aja_jaj,我们可以视为它们的贡献都单独为φ(x)\varphi(x)φ(x))
那么我们可以把3∼n3\sim n3∼n按照φ\varphiφ排序从小到大加就好了。
至于1,21,21,2,特判一下比较小的情况就可以了,kkk比较大时显然1,21,21,2会被选上去,需要多选222个。
时间复杂度:O(nlogn)O(n\log n)O(nlogn)
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;
}