挺简单的一道概率dp+简单数论+简单容斥题目。

由于是编译器小白,枚举了n次编译器qwq。。。

c++的同学建议用clang++17那个编译器qwq

一.概率dp+简单数论部分

比较容易想到的,我们设表示当前gcd为i,期望步后到达1

初始化

然后,我们来推下转移方程:

明显的有,

那么,我们只要分两种情况分开讨论即可。

注意到,当且仅当,也即是共有

时,即我们设

那么将原转移方程移项后有:

因为n和al的值都可以O(1)获得,那么,我们只需要计算出al就好了。

我们直接枚举j肯定不行的,会T惨,那么,我们考虑枚举

因为,有一定是i的约数,所以我们枚举i的所有约数即可

设当前枚举的为k

那么,我们现在需要计算的就是,究竟有多少个使得

那么,我们用式子表达出来就是

这就等价于求

即是求中,与互质的数的个数

二.简单数论+简单容斥部分

为了好看,我们设

那么,我们现在就需要求出中与x互质的数的个数。

这个怎么做呢?

我们把x分解质因数:

那么,一个数要与x互质,必须不是的倍数,那么我们只需减去C中的倍数的数字即可。直接容斥就好了!

那么复杂度呢?直接容斥不会超时吗?

我们分析下,因为单次求,复杂度只和质数的个数有关,所以,我们直接选最小的几个质数就行了。

因为有:=510510<100000

所以,一个数最多只能分出6个不同的质数出来,所以单词容斥的上限是,而这明显是跑不满的,所以不会超时。

于是,到此,我们求出了所有的dp值了,那么答案怎么求呢?

很简单,我们枚举我们第一个放进去的数即可,那么,答案就是:

于是此题解完。(除的部分记得用逆元乘法代替)

代码:

#include<stdio.h>
#include<cmath>
using namespace std;
const int mod=1e9+7,N=1e5+1;
int dp[N],n,g[N],f[N],e,tim;
int vis[N],sav[N],ep,ans,m;
inline void sai(){//为了分解质因数,处理出一个数的任意质因数函数g
    for(int i=2;i<N;++i){
        if(!g[i]){
            f[++e]=i;g[i]=i;
        }
        for(int j=1;j<=e;++j){
            if(i*f[j]>=N)break;
            g[i*f[j]]=f[j];
            if(i%f[j]==0)break;
        }
    }
}
inline void dfs(int now,int s,int num){//容斥
    if(now==ep+1){
        if(num){
            if(num&1){
                ans-=(m/s);
            }else{
                ans+=(m/s);
            }
        }
        return;
    }
    dfs(now+1,s,num);
    dfs(now+1,s*sav[now],num+1);
}
inline int calc(int x,int y){//计算1-n中与x的gcd为y的数的个数
    ++tim;ep=0;
    x/=y;m=n/y;ans=m;
    while(g[x]){
        if(vis[g[x]]!=tim)sav[++ep]=g[x],vis[g[x]]=tim;
        x/=g[x];
    }
    dfs(1,1,0);
    return ans;
}
inline int ksm(int x,int y){
    int ans=1;
    while(y){
        if(y&1)ans=(1LL*ans*x)%mod;
        x=(1LL*x*x)%mod;y>>=1;
    }
    return ans;
}
int main(){
    sai();
    dp[1]=0;
    scanf("%d",&n);
    for(int i=2;i<=n;++i){
        int tot=n/i,al=0;
        for(int j=sqrt(i);j;--j){//枚举gcd
            if(i%j==0){
                (al+=(1LL*dp[j]*calc(i,j))%mod)%=mod;
                if(i!=1&&i/j!=j){
                    (al+=(1LL*dp[i/j]*calc(i,i/j))%mod)%=mod;
                }
            }
        }
        dp[i]=(1LL*(n+al)*ksm(n-tot,mod-2))%mod;
    }
    ans=0;
    for(int i=1;i<=n;++i){
        ans=(ans+dp[i])%mod;
    }
    ans=(1LL*ans*ksm(n,mod-2)+1)%mod;
    printf("%d",ans);
    return 0;
}