【CF1139D】Steps to One
阿新 • • 發佈:2020-10-26
題目
題目連結:https://codeforces.com/problemset/problem/1139/D
給一個數列,每次隨機選一個 \(1\) 到 \(n\) 之間的數加在數列末尾,數列中所有數的 \(\gcd=1\) 時停止,求期望長度。
思路
設 \(f[i]\) 表示數列 \(\gcd\) 等於 \(i\) 的時候,期望多少步會讓 \(\gcd=1\)。
那麼顯然可以列舉 \(i\) 的每一個因子來轉移。
其中 \(g(i,j)\)
那麼有 \[g(i,j)=\sum_{d|j}\mu(d)\lfloor\frac{i}{d}\rfloor \]
那麼可以先將 \(i\) 的因子扔到一個 vector 中。由於最終每個數 \(k\) 作為因子會被列舉 \(\lfloor\frac{n}{k}\rfloor\) 次,所以均攤時間複雜度是 \(O(\log n)\) 的。
轉移時發現等號右邊也含有 \(f[i]\),所以拆開扔到左邊去
然後就可以 \(O(n\log^2 n)\) 做了。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010,MOD=1e9+7; int n,m,mu[N],prm[N]; ll ans,f[N],h[N]; bool v[N]; vector<int> d[N]; void findprm(int n) { mu[1]=1; for (int i=2;i<=n;i++) { if (!v[i]) prm[++m]=i,mu[i]=-1; for (int j=1;j<=m;j++) { if (i>n/prm[j]) break; v[i*prm[j]]=1; mu[i*prm[j]]=-mu[i]; if (i%prm[j]==0) { mu[i*prm[j]]=0; break; } } } } ll fpow(ll x,ll k) { ll ans=1; for (;k;k>>=1,x=x*x%MOD) if (k&1) ans=ans*x%MOD; return ans; } ll calc(ll n,ll m) { ll s=0; for (int i=0;i<d[m].size();i++) s=(s+mu[d[m][i]]*(n/d[m][i]))%MOD; return s; } int main() { findprm(N-10); scanf("%lld",&n); for (int i=1;i<=n;i++) for (int j=i;j<=n;j+=i) d[j].push_back(i); f[1]=1; ans=fpow(n,MOD-2); for (int i=2;i<=n;i++) { for (int j=0;j<d[i].size()-1;j++) f[i]=(f[i]+f[d[i][j]]*calc(n/d[i][j],i/d[i][j]))%MOD; f[i]=(n+f[i])*fpow(n-calc(n/i,1),MOD-2)%MOD; ans=(ans+f[i]*fpow(n,MOD-2))%MOD; } printf("%lld",ans%MOD); return 0; }