codeforces#1139D. Steps to One (概率dp+莫比乌斯反演)

题目链接:

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

题意:

 在$1$到$m$中选择一个数,加入到一个初始为空的序列中,当序列的$gcd$和为$1$时,停止加入,求序列的期望长度

数据范围:

$1 \leq m \leq 10^{9}$

分析:

 定义$f[x$]为$gcd$等于$x$时把序列$gcd和$改变成1的期望长度,定义$G(x,y)$为$i$在1到$n$范围,满足$gcd(x,i)=y$,$i$的数量,得到以下公式:

$$f[i]=1+\frac{\sum_{d|i}f[d]\times G(i,d)}n,x=i$$

由于公式两边都有$f[i]$(因为$i|i$),所以对公式进行变换:

$$f[i]=1+\frac{\sum_{d|i,d<i}f[d]\times G(i,d)+f[i]\times G(i,i)}n,x=i$$
$$\frac{n-G(i,i)}{n}\cdot f[i]=1+\frac{\sum_{d|i,d<i}f[d]\times G(i,d)}n,x=i$$
$$f[i]=\frac{n+\sum_{d|i,d<i}f[d]\times G(i,d)}{n-G(i,i)},x=i$$

 然后就是用莫比乌斯反演快速求出$G(x,y)$即可

$$G(x,y)=\sum_{d|\frac xy}\mu(d)\left\lfloor\frac{n}{d*y}\right\rfloor$$

ac代码:

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
const int mod=1e9+7;
ll f[maxn],n;
int pri[maxn],mu[maxn],is[maxn],cnt;
vector<int>ve[maxn];
int cal(int x,int y)//i 在 1-n gcd(x,i)=y;
{
    int dd=x/y,res=0;
    for(int i=0;i<ve[dd].size();i++)
    {
        int p=ve[dd][i];
        res=res+mu[p]*(n/y)/p;
    }
    return res;
}
ll qpow(ll x,ll y)
{
    ll res=1,k=x;
    while(y)
    {
        if(y&1)res=res*k%mod;
        k=k*k%mod;
        y/=2;
    }
    return res;
}
int getu()
{
    mu[1]=1;
    for(int i=2;i<=n;++i)
    {
        if(!is[i])
        {
            pri[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&i*pri[j]<=n;++j)
        {
            is[i*pri[j]]=1;
            if(i%pri[j])
                mu[i*pri[j]]=-mu[i];
            else
            {
                mu[i*pri[j]]=0;
                break;
            }
        }
    }
}
void init()
{
    getu();
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j+=i)ve[j].push_back(i);
}
int main()
{
    ll ans=0;
    scanf("%d",&n);
    init();
    for(int i=1;i<=n;i++)
    {
        f[i]=n;
        for(int j=0;j<ve[i].size();j++)
        {
            int d=ve[i][j];
            if(i==d)continue;
            f[i]=(f[i]+f[d]*cal(i,d)%mod)%mod;
        }
        f[i]=f[i]*qpow(n-cal(i,i),mod-2)%mod;
        ans=(ans+f[i])%mod;
    }
    printf("%d\n",(ans*qpow(n,mod-2)%mod+1)%mod);
	return 0;
}

  

 

posted @ 2019-04-11 14:02  czh~  阅读(248)  评论(0编辑  收藏  举报