2300专项:D. Steps to One(dp 莫比乌斯)

原题: http://codeforces.com/problemset/problem/1139/D

题意:

给出一个n(<=1e5)和空的数组,你每次会选出1~m中的任意一个数放入数组,当数组的gcd等于1时结束,问数组长度的期望。

解析:

问题一: 1~n中有多少个数与x的gcd等于d

F ( d ) = i = 1 n [ g c d ( x , i ) = d ] F(d)=\sum_{i=1}^n [gcd(x,i)=d] G ( d ) = F ( k d ) G(d)=\sum F(kd)
显然有: G ( d ) = n / d G(d)=n/d
莫比乌斯反演: F ( d ) = u ( k ) G ( k d ) F(d)=\sum u(k)G(kd)

因为 F ( d ) F(d) 的定义要求 d x d|x ,而一部分 k d kd 虽然是d的倍数,但是不是x的因子,这部分的答案显然是0。

所以这个问题的答案为: F ( d ) = u ( k ) [ n / ( k d ) ] [ ( k d ) x ] F(d)=\sum u(k)*[n/(k*d)]*[(k*d)|x]

而找哪部分 ( k d ) x (k*d)|x 只需要找 k x d k|\frac{x}{d} 即可。

问题二: 如何求解

d p [ x ] dp[x] g c d = x gcd=x 到达 g c d = 1 gcd=1 的期望步数,转移方程为:
d p [ x ] = 1 + d x F ( d ) d p [ d ] n dp[x]=1+\dfrac{\sum_{d|x}F(d)*dp[d]}{n}
就是说,x走一步后的结果的平均期望。这个 d d 是包含 x x 本身的,所以得:
d p [ x ] = 1 + d x d ! = x F ( d ) d p [ d ] n + F ( x ) d p [ x ] n ( n F ( x ) ) d p [ x ] = n + d x d ! = x F ( d ) d p [ d ] dp[x]=1+\dfrac{\sum_{d|x}^{d!=x}F(d)*dp[d]}{n}+\dfrac{F(x)dp[x]}{n}\\(n-F(x))dp[x]=n+\sum_{d|x}^{d!=x}F(d)*dp[d]

最后,第一步每个情况都有 1 / n 1/n 的概率走到,所以答案为 i = 1 n d p [ i ] n + 1 \dfrac{\sum_{i=1}^ndp[i]}{n}+1

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
const int maxn=100009;
const LL mod=1e9+7;
int u[maxn];
int pri[maxn>>1],now;
bool vis[maxn];
int g[maxn],f[maxn];
int n;
LL Pow(LL a,LL b){
    LL res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;b>>=1;
    }
    return res;
}
void init(){
    u[1]=1;
    rep(i,2,n){
        if(!vis[i]){
            pri[++now]=i;
            u[i]=-1;
        }
        for(int j=1;j<=now&&pri[j]*i<=n;j++){
            vis[pri[j]*i]=1;
            if(i%pri[j]==0){
                u[pri[j]*i]=0;
                break;
            }
            u[pri[j]*i]=-u[i];
        }
    }
}

LL dp[maxn];
vector<int>son[maxn];
int deal(int x,int d){
    x/=d;
    int res=0;
    rep(i,0,son[x].size()-1){
        int j=son[x][i];
        res+=u[j]*(n/j/d);
    }
    res+=u[x]*(n/x/d);
    return res;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    init();
    rep(i,1,n){
        for(int j=i+i;j<=n;j+=i){
            son[j].push_back(i);
        }
    }
    LL invn=Pow(n,mod-2);
    LL ans=0;
    rep(i,1,n){
        LL sum=n;
        rep(j,0,son[i].size()-1){
            int s=son[i][j];
            sum=(sum+deal(i,s)*dp[s])%mod;
        }

        sum=sum*Pow(n-(n/i),mod-2)%mod;
        dp[i]=sum;

        ans=(ans+dp[i])%mod;
    }
    cout<<(ans*invn%mod+1)%mod<<endl;
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/89400014