【CF1208G】Polygons(欧拉函数+贪心)
- 给定 \(n,k\),要求选择 \(3\sim n\) 中 \(k\) 个不同的正整数 \(a_i\)。
- 给定一个圆,求至少需要在圆上选出多少个点,才能利用这些点构造出所有的正 \(a_i\) 边形。
- \(3\le n\le10^6\)
最优构造方案
考虑对于已知的若干 \(a_i\),构造出至少需要多少的点。
首先肯定存在一种最优构造方案,满足所有多边形有一个共同顶点。以这个位置作为 \(0\),则正 \(d\) 边形对应的所有顶点位置依次为 \(0,\frac nd,2\times\frac nd,\cdots,(d-1)\times\frac nd\)。
于是发现,\(0,\frac nd,2\times\frac nd,\cdots,(d-1)\times\frac nd\) 被标记,当且仅当存在 \(a_i\) 是 \(d\) 的倍数。
除去一个数的因数的贡献后,就变成若存在 \(a_i\) 是 \(d\) 的倍数,就能产生 \(\phi(d)\) 的贡献。
贪心选择
根据上面的结论,由于选择一个数会让它的所有因数都被标记,我们肯定优选选择它的因数。那么选择一个数 \(x\) 后新被标记的就只有它自身,也就是带来 \(\phi(x)\) 的贡献。
所以只要将所有 \(\phi(x)\) 排序,从小到大选择即可。(显然一个数的因数的 \(\phi\) 一定比它小)
注意特殊考虑标记 \(1\) 和 \(2\),其中 \(1\) 一定会被标记,\(2\) 在 \(k\ge 2\) 的时候会被标记(第一个选择 \(3\);第二个选择 \(4\) 或 \(6\),二者都是 \(2\) 的倍数)。
代码:\(O(n\log n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 1000000
using namespace std;
int n,k,Pt,P[N+5],phi[N+5];I void Sieve(CI n)//线性筛预处理欧拉函数
{
phi[1]=1;for(RI i=2,j;i<=n;++i) for(!P[i]&&(phi[P[++Pt]=i]=i-1),j=1;j<=Pt&&i*P[j]<=n;++j)
if(P[i*P[j]]=1,i%P[j]) phi[i*P[j]]=phi[i]*(P[j]-1);else {phi[i*P[j]]=phi[i]*P[j];break;}
}
int main()
{
RI i;long long t=0;for(scanf("%d%d",&n,&k),Sieve(n),sort(phi+3,phi+n+1),i=3;i<=2+k;++i) t+=phi[i];//排序后贪心选择
return ++t,k>=2&&++t,printf("%lld\n",t),0;//特殊考虑标记1和2
}
待到再迷茫时回头望,所有脚印会发出光芒