1. 程式人生 > 實用技巧 >【CF1139D】Steps to One

【CF1139D】Steps to One

題目

題目連結:https://codeforces.com/problemset/problem/1139/D
給一個數列,每次隨機選一個 \(1\)\(n\) 之間的數加在數列末尾,數列中所有數的 \(\gcd=1\) 時停止,求期望長度。

思路

\(f[i]\) 表示數列 \(\gcd\) 等於 \(i\) 的時候,期望多少步會讓 \(\gcd=1\)
那麼顯然可以列舉 \(i\) 的每一個因子來轉移。

\[f[i]=1+\frac{1}{m}\sum_{d|i}f[j]\times g(\lfloor\frac{n}{d}\rfloor,\frac{i}{d}) \]

其中 \(g(i,j)\)

表示 \(1\sim i\) 中與 \(d\) 互質的數的個數。
那麼有

\[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]\),所以拆開扔到左邊去

\[(\frac{n-g(\lfloor\frac{n}{i}\rfloor,1)}{n})f[i]=1+\frac{1}{n}\sum_{d|i}f[d]\times g(\lfloor\frac{n}{d}\rfloor,\frac{i}{d}) \]

\[f[i]=\frac{n+\sum_{d|i}f[d]\times g(\lfloor\frac{n}{d}\rfloor,\frac{i}{d})}{n}\times \frac{n}{n-g(\lfloor\frac{n}{i}\rfloor,1)} \]

\[f[i]=\frac{n+\sum_{d|i}f[d]\times g(\lfloor\frac{n}{d}\rfloor,\frac{i}{d})}{n-g(\lfloor\frac{n}{i}\rfloor,1)} \]

然後就可以 \(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;
}