【题解】P5589 小猪佩奇玩游戏(期望)
类似于这道题,根据题意建立一张dag图,每个无交的dag之间相互不影响。考虑算一个dag的期望删除步数。
假设一个点有\(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\)的数,然后枚举它所有的幂然后暴力计算即可。但是要去重,具体实现用哈希。
复杂度\(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; }