# Codeforces Round 548 (Div. 2) (D)
> https://codeforces.com/contest/1139/problem/D
給一個整數$m$,現在有個空序列,每次操作為隨機從$[1,m]$中取一數加進序列中,如果整個序列之$gcd$等於1則停下,求序列期望長度模$mod$。$1 \le m \le 10^5$。
這題狀態還蠻好想的,定義$dp(i)$為目前序列之$gcd$為$i$時執行操作到結束的期望長度,假如$i=6$則我們可以寫出:
\begin{array}{ll}
dp(6) = 1 + \frac{1}{m} \left[
F(6, 1) \times dp(1) +
F(6, 2) \times dp(2) + \\
F(6, 3) \times dp(3) +
F(6, 6) \times dp(6)
\right],
\end{array}
其中$F(i, j)$為1到m中有幾個數使得其和$i$的$gcd$等於$j$,$j$必須是$i$的因數,於是有:
\begin{array}{ll}
F(6, 6) = \left \lfloor \frac{m}{6} \right \rfloor, \\
F(6, 3) = \left \lfloor \frac{m}{3} \right \rfloor - \left \lfloor \frac{m}{6} \right \rfloor, \\
F(6, 2) = \left \lfloor \frac{m}{2} \right \rfloor - \left \lfloor \frac{m}{6} \right \rfloor, \\
F(6, 1) = m - \left \lfloor \frac{m}{2} \right \rfloor - \left \lfloor \frac{m}{3} \right \rfloor + \left \lfloor \frac{m}{6} \right \rfloor,
\end{array}
其實就是對$i/j$的質因數做容斥,如果要算整個$dp(i)$的話,我們可以考慮枚舉每個$i$的因數$j$,對$dp(i)$的貢獻有$\left \lfloor \frac{m}{j} \right \rfloor dp(j/j的質因數乘積)$這東西,如果是奇數個質因數就扣掉,否則就加,這邊複雜度應為$\sigma_0(i)2^6 \times 6$,$\sigma_0(i)$為$i$之因數個數,然後$\le 10^5$最多質因數有6個。
假如目前為空序列,則我們操作一次,假如說我們得到x則表示目前序列之$gcd$等於x,於是我們可以得到所求應為:
\begin{array}{ll}
\sum_{k=1}^{m} \frac{dp(k)}{m},
\end{array}
其中$dp(1) \equiv 1$。
如何求$dp(i)$呢?列一下式子:
\begin{array}{ll}
&dp(i) = 1 + \frac{1}{m} \sum_{d|i}F(i, d)dp(d) \\
\Rightarrow &dp(i) (1 - \frac{F(i, i)}{m}) = 1 + \frac{1}{m} \sum_{d|i, d<i} F(i, d) dp(d) \\
\Rightarrow &dp(i)(m - F(i, i)) = m + \sum_{d|i, d<i} F(i, d) dp(d),
\end{array}
預處理每個數的因數和質因數,於是1到m中因數個數和有$mlog(m)$個複雜度應該過得去。
```cpp=
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define ll long long
#define pii pair<int, int>
#define pll pair<long long, long long>
#define F first
#define S second
#define endl '\n'
using namespace std;
const int inf = 0x3f3f3f3f;
const ll Inf = 1e18;
const int mod = 1e9 + 7;
const int N = 1e5 + 5;
vector<vector<int>> d(N), pd(N);
const int maxn = 1e5+5;
vector<bool> p(maxn, 1);
vector<int> prime;
ll inv[maxn];
void init(){
p[0] = p[1] = 0;
for(int i=2; i<maxn; i++){
if(p[i]){
prime.push_back(i);
for(ll j=1LL*i*i; j<1LL*maxn; j+=i){
p[j] = 0;
}
}
}
inv[0] = inv[1] = 1;
for(int i=2; i<maxn; i++){
inv[i] = mod - mod/i * inv[mod%i] % mod;
}
return;
}
void solve(){
int m; cin >> m;
for(int i=1; i<=m; i++){
for(int j=i; j<=m; j+=i){
d[j].push_back(i);
}
}
init();
for(auto i : prime){
for(int j=i; j<=m; j+=i){
pd[j].push_back(i);
}
}
ll dp[m+1];
dp[1] = 1;
for(int i=2; i<=m; i++){
ll tmp = m;
for(auto j : d[i]){
int l = pd[j].size();
for(int k=0; k<(1<<l); k++){
int cur = 1;
for(int kk=0; kk<l; kk++){
if(~(k>>kk) & 1) continue;
cur *= pd[j][kk];
}
if(j == i && cur == 1) continue;
tmp = (tmp + (__builtin_popcount(k) & 1 ? -1LL : 1LL) * (m/j) * dp[j/cur]) % mod;
}
}
if(tmp < 0) tmp += mod;
dp[i] = inv[m - m/i] * tmp % mod;
}
ll ans = 0;
for(int i=1; i<=m; i++) ans = (ans + dp[i]) % mod;
ans = ans * inv[m] % mod;
cout << ans << endl;
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
int t = 1;
// cin >> t;
while(t--)
solve();
return 0;
}
```
這題還有莫比烏斯函數的解法,可以參考:
> https://codeforces.com/blog/entry/66101?#comment-501072
> https://codeforces.com/blog/entry/66101?#comment-500999
### 時間複雜度 :$O(mlog(m)) \times 2^6 \times 6$
###### tags: `number theory` `math` `dp`