Codeforces Round #548 (Div. 2) D. Steps to One

链接

https://codeforces.com/contest/1139/problem/D

题解

我好菜啊
本来想回紫,结果在掉分的路上渐行渐远
首先可以直接写出期望 d p dp dp方程
f i f_i fi表示我写的序列的最大公约数已经等于 i i i的情况下,写到结束为止的期望长度
那么 f i = 1 + 1 m ∑ j = 1 n f g c d ( i , j ) f_i=1+\frac{1}{m}\sum_{j=1}^nf_{gcd(i,j)} fi=1+m1j=1nfgcd(i,j)
移项整理得
m − ⌊ m i ⌋ m f i = 1 + 1 m ∑ j = 1 n f g c d ( i , j ) [ ( i , j ) ̸ = n ] \frac{m-\lfloor\frac{m}{i}\rfloor}{m}f_i=1+\frac{1}{m}\sum_{j=1}^nf_{gcd(i,j)}[(i,j)\not = n] mmimfi=1+m1j=1nfgcd(i,j)[(i,j)̸=n]
直接 d p dp dp的复杂度是 O ( m 2 ) O(m^2) O(m2)
会超时
那我就统计一下 i i i的每种约数对 i i i的贡献
即先枚举 x x x(作为约数),再去枚举 x x x的倍数 n n n
现在就是要求一个
∑ i = 1 m [ ( n , i ) = x ] \sum_{i=1}^m[(n,i)=x] i=1m[(n,i)=x]
反演得 ∑ d ∣ n μ ( d ) ⌊ m d ⌋ \sum_{d|n}\mu(d)\lfloor\frac{m}{d}\rfloor dnμ(d)dm
预处理 n n n的约数就行了

代码

#include <bits/stdc++.h>
#define eps 1e-8
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define maxn 200010
#define base 2000
#define cl(x) memset(x,0,sizeof(x))
#define mod 1000000007ll
using namespace std;
typedef long long ll;
ll read(ll x=0)
{
	ll c, f=1;
	for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
	for(;isdigit(c);c=getchar())x=x*10+c-48;
	return f*x;
}
ll fastpow(ll a, ll b)
{
	ll ans=1, t=a;
	for(;b;b>>=1,t=t*t%mod)if(b&1)ans=ans*t%mod;
	return ans;
}
struct fs
{
	ll a, b;
	fs(ll a, ll b):a(a),b(b){}
	fs(){}
}f[maxn], ans;
fs operator+(fs x, fs y)
{
	return fs((x.a*y.b+x.b*y.a)%mod,x.b*y.b%mod);
}
fs operator*(fs x, fs y)
{
	return fs((x.a*y.a)%mod,(x.b*y.b)%mod);
}
ll M, phi[maxn], mark[maxn], prime[maxn], mu[maxn];
vector<ll> ys[maxn];
void init()
{
	ll i, j;
	phi[1]=1;
	mu[1]=1;
	for(i=2;i<maxn;i++)
	{
		if(!mark[i])
		{
			prime[++*prime]=i;
			phi[i]=i-1;
			mu[i]=-1;
		}
		for(j=1;j<=*prime and i*prime[j]<maxn;j++)
		{
			mark[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*phi[prime[j]], mu[i*prime[j]]=-mu[i];
		}
	}
	for(i=1;i<maxn;i++)for(j=i;j<maxn;j+=i)
	{
		ys[j].push_back(i);
	}
}
ll calc(ll m, ll n)
{
	ll ans=0, i, d;
	for(i=0;i<ys[n].size();i++)
	{
		d=ys[n][i];
		ans=(ans+mu[d]*(m/d))%mod;
	}
	return (ans%mod+mod)%mod;
}
int main()
{
	ll i, j, k, cnt;
	M=read();
	init();
	for(i=1;i<=M;i++)f[i]=fs(0ll,1ll);
	fs ans(0ll,1ll);
	for(i=1;i<=M;i++)
	{
		if(i>1)
		{
			f[i]=fs(1ll,M)*f[i]+fs(1ll,1ll);
			f[i]=f[i]*fs(M,M-M/i);
		}
		for(j=i+i;j<=M;j+=i)
		{
			cnt=calc(M/i,j/i);
			f[j]=f[j]+fs(cnt,1ll)*f[i];
		}
	}
	for(i=1;i<=M;i++)
	{
		ans=ans+(f[i]+fs(1ll,1ll))*fs(1ll,M);
	}
	cout<<ans.a*fastpow(ans.b,mod-2)%mod;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值