• 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] imes G(i,d)}n,x=i$$

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

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

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

    $$G(x,y)=sum_{d|frac xy}mu(d)leftlfloorfrac{n}{d*y} ight floor$$

    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
    ",(ans*qpow(n,mod-2)%mod+1)%mod);
    	return 0;
    }
    

      

  • 相关阅读:
    代码规范
    svn的牛逼操作反向merge
    QT 半透明遮罩(弹窗)
    ACE库 ACE_Handle_Set类解析
    linux系统如何启用ftp服务
    vim color
    Linux动态库应用
    自建工程makefile文件
    Makefile工程文件
    linux杂记
  • 原文地址:https://www.cnblogs.com/carcar/p/10689260.html
Copyright © 2020-2023  润新知