YbtOJ-選點構形【尤拉函式】
阿新 • • 發佈:2022-02-20
正題
題目連結: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_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; }