【題解】P5589 小豬佩奇玩游戲(期望)
假設一個點有\(x\)個點(包括自己)可以到達他,他就對答案有\(1/x\)的貢獻。這是因為這個點必須被刪掉而通過刪掉這個點本身刪掉這個點的概率是\(1/x\),所以對期望的貢獻是\(1\times 1/x\)。
如何算\(x\)。\(x\)是唯一分解之后所有的指數的\(gcd\)的約數個數。
此時就有60分了。
考慮對於\(\le \sqrt n\)的數暴力處理,對於\(> \sqrt n\)的數,由於這些數不會有出度,所以只要算他們的入度。眾所周知形如\(x^y\)的整數是很少的。具體的,大概是:
\[O(\sum_{i=1}^{\sqrt n} \log_i n) \]
實在是太小了!所以我們定位一個\(\le \sqrt n\)的數,然后枚舉它所有的冪然后暴力計算即可。但是要去重,具體實現用哈希。
還有一些出度入度都為\(0\)的點,直接+1
復雜度\(O(\text{隨便過})\)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<unordered_set>
using namespace std; typedef long long ll; typedef long double lb;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e7+5;
int n,usd[maxn],Min[maxn];
vector<int> e;
inline void pre(const int&n){
usd[1]=1; Min[1]=10;
for(int t=2;t<=n;++t){
if(!usd[t])e.push_back(t),Min[t]=t;
for(auto i:e){
if(1ll*i*t>n)break;
usd[i*t]=1,Min[i*t]=i;
if(t%i==0)break;
}
}
}
int gcd(const int&x,const int&y){return y?gcd(y,x%y):x;}
inline int getd(int x){
if(x==1)return 1;
int d=0;
while(x>1){
int g=Min[x],cnt=0;
while(x%g==0&&x>1) ++cnt,x/=g;
d=gcd(d,cnt);
}
return d;
}
int K=0;
inline lb getsig(int x){
if(x==1) return 1;
ll ret=1;
while(x>1){
K=max(K,x);
int g=Min[x],cnt=0;
while(x%g==0&&x>1) ++cnt,x/=g;
ret=ret*(cnt+1LL);
}
return (lb)1/ret;
}
unordered_set<int> s;
int main(){
pre(maxn-1);
int T=qr();
while(T--){
int n=qr(),N=sqrt(n);
lb ans=0;
for(int t=1;t<=N;++t) ans+=getsig(getd(t));
ll k=2;
for(int t=2,cnt;t<=N;k=++t){
int d=getd(t);
cnt=1;
while(k<=N) k=k*t,++cnt;
while(k<=n){
if(s.find(k)==s.end())
s.insert(k),ans+=getsig(d*cnt);
k=k*t,++cnt;
}
}
ans+=(n-N-s.size());
printf("%Lf\n",ans);
s.clear();
}
return 0;
}