【题解】P5589 小猪佩奇玩游戏(期望)
【题解】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;
}
博客保留所有权利,谢绝学步园、码迷等不在文首明显处显著标明转载来源的任何个人或组织进行转载!其他文明转载授权且欢迎!