Loading

CF1139D Steps to One

CF1139D Steps to One

这,暴力写了2h还没写出来,正解写了10min就一遍AC了

话说我没想到 \(1\)\(n\) 中的因数总数是 \(O(n\ln n)\) 级别是不是该被毙掉啊

暴力

首先设个 \(dp_i\) 表示当前 \(\gcd\)\(i\) ,需要 \(dp_i\) 次变成 \(1\)

\(ans=1+\dfrac{\sum_{i=1}^{m}dp_i}{m}\)

前面那个 \(1\) 只要自己打个暴力就很好理解了,因为一开始没有数。

如果你不知道为啥这么设,多试几次就知道这么设有种能往下推的感觉

发现如果连边 \(i\to factor_i\) 是个 \(DAG\) ,那么模拟建反图从 \(1\) 往上转移可得 \(dp_i=1+\dfrac{\sum_{j=1}^{m}dp[\gcd(i,j)]}{m}\)

然后我决定写个暴力验证一下,然后一调就是2h。因为右边也有 \(dp_i\) ,所以要把右边 \(\dfrac{m}{i}\)\(dp_i\) 移过来。

这就是暴力唯一的价值

正解

既然都要移过来了,那还不如推正解。

\[\sum_{i=1}^{m}dp[\gcd(i,k)]\\ =\sum_{d|k}dp[d]\sum_{i=1}^{m}[\gcd(i,k)==d]\\ =\sum_{d|k}dp[d]\sum_{i=1}^{\frac{m}{d}}[\gcd(i,\dfrac{k}{d})==1]\\ =\sum_{d|k}dp[d]\sum_{i=1}^{\frac{m}{d}}\sum_{t|\gcd(i,\frac{k}{d})}\mu(t)\\ =\sum_{d|k}dp[d]\sum_{t|\frac{k}{d}}\mu(t)\lfloor\dfrac{m}{dt}\rfloor\\ =\sum_{T|k}\dfrac{m}{T}\sum_{d|T}dp[d]\mu(\dfrac{T}{d}) \]

带回去:

\[dp[i]=1+\dfrac{\sum_{T|i}\dfrac{m}{T}\sum_{d|T}dp[d]*\mu(\dfrac{T}{d})}{m} \]

然后把那几个 \(dp_i\) 提出来移过去

\[dp[i]=1+\dfrac{\sum_{T|i}\dfrac{m}{T}\sum_{d|T,d<i}dp[d]*\mu(\dfrac{T}{d})}{m}+\dfrac{\lfloor\dfrac{m}{i}\rfloor dp[i]}{m}\\ (m-\dfrac{m}{i})dp[i]=m+\sum_{T|i}\dfrac{m}{T}\sum_{d|T,d<i}dp[d]*\mu(\dfrac{T}{d})\\ dp[i]=\dfrac{m+\sum_{T|i}\dfrac{m}{T}\sum_{d|T,d<i}dp[d]*\mu(\dfrac{T}{d})}{m-\dfrac{m}{i}} \]

维护 \(f_T=\sum_{d|T}dp[d]*\mu(\dfrac{T}{d})\)

发现只要先更新 \(dp\) 再更新 \(f\) 就可以避开 \(d<i\) 这个奇怪的东西了

考虑如何快速更新 \(f\) ,我们发现只要枚举 \(i|T\) ,修改所有 \(T\) 那么均摊下来总复杂度是 \(\sum_{i=1}^{n}\dfrac{n}{i}=n\ln n\)

然后就是我傻逼的地方了,总因数个数也是 \(O(n\ln n)\) 级别的,直接预处理出来即可

复杂度 \(O(n\ln n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define mkp(x,y) make_pair(x,y)
#define fi first
#define se second
#define pb(x) push_back(x)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
#define N 100005
#define mod 1000000007
int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*res*n%mod;return res;}
void fmod(int&x){x+=x>>31&mod,x-=mod,x+=x>>31&mod;}
int m,mu[N],pri[N],cnt,f[N],dp[N],inv[N],ans;
bool vis[N];
vector<int>d[N];
void Sieve(const int&K){
	mu[1]=1;
	for(int i=2;i<=K;++i){
		if(!vis[i])mu[i]=-1,pri[++cnt]=i;
		for(int j=1;i*pri[j]<=K;++j){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=K;++i)
		for(int j=1;i*j<=K;++j)
			d[i*j].pb(i);
	inv[1]=1;
	for(int i=2;i<=K;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
signed main(){
	cin>>m,Sieve(m);
	dp[1]=0;
	for(int i=2;i<=m;++i){
		int res=m;
		for(int j:d[i])
			fmod(res+=1ll*(m/j)*f[j]%mod);
		dp[i]=1ll*res*inv[m-m/i]%mod;
		for(int j=1;i*j<=m;++j)
			fmod(f[i*j]+=dp[i]*mu[j]);
	}
	ans=m;
	for(int i=1;i<=m;++i)fmod(ans+=dp[i]);
	printf("%lld\n",1ll*ans*inv[m]%mod);
	return 0;
}

\(\color{black}{\texttt{F}}\color{red}{\texttt{orever_Pursuit}}\) 10min就秒了这题,还是 \(O(n)\) 的,我被吊打了/ll

posted @ 2020-10-30 21:22  zzctommy  阅读(99)  评论(0编辑  收藏  举报