CF1139D Steps to One 题解【莫比乌斯反演】【枚举】【DP】
反演套 DP 的好题(不用反演貌似也能做
Description
Vivek initially has an empty array \(a\) and some integer constant \(m\).
He performs the following algorithm:
- Select a random integer \(x\) uniformly in range from \(1\) to \(m\) and append it to the end of \(a\).
- Compute the greatest common divisor of integers in \(a\).
- In case it equals to \(1\), break
- Otherwise, return to step \(1\).
Find the expected length of \(a\). It can be shown that it can be represented as \(\frac PQ\) where \(P\) and \(Q\) are coprime integers and \(Q\neq 0\pmod{10^9+7}\). Print the value of \(P\cdot Q^{-1}\pmod{10^9+7}\).
Input
The first and only line contains a single integer \(m\)(\(1\le m\le 100000\)).
Output
Print a single integer — the expected length of the array \(a\) written as \(P\cdot Q^{-1}\pmod{10^9+7}\).
Examples
input
1
output
1
input
2
output
2
input
4
output
333333338
Note
In the first example, since Vivek can choose only integers from \(1\) to \(1\), he will have \(a=[1]\) after the first append operation, and after that quit the algorithm. Hence the length of \(a\) is always \(1\), so its expected value is \(1\) as well.
In the second example, Vivek each time will append either \(1\) or \(2\), so after finishing the algorithm he will end up having some number of \(2\)'s (possibly zero), and a single \(1\) in the end. The expected length of the list is \(1⋅\frac 12+2⋅\frac 1{2^2}+3⋅\frac 1{2^3}+\dots =2\).
题意
每一步在 \(1\sim m\) 中任选一个整数,问期望多少步后选出的数的最大公约数是 \(1\)。答案对 \(1\ 000\ 000\ 007\) 取模。
题解
因为每一步不会让已经选了的元素的 \(\gcd\) 和变大,因此认为是一个除自环外的有向无环图。对于自环我们很好处理,所以把它看成是一道期望 DP。
令 \(f[i]\) 表示当前的 \(\gcd\) 和为 \(i\),到 \(\gcd\) 和为 \(1\) 的状态的期望步数。因此把状态转移方程写出来
\]
这样的转移是 \(O(m^2)\) 的。但是我们发现,对于很多 \(j\),\(\gcd(i,j)\) 都是相等的,因此我们把这样的数整合到一起。
令 \(F(n)\) 表示 \(1\sim m\) 中有多少个数 \(i\) 满足 \(\gcd(x,i)=n\),其中视 \(x\) 为常数。
则计算 \(f[i]\) 就转化为了
\]
这样差不多就把枚举优化到了 \(\log n\) 的 \(d|i\)。
考虑怎么计算 \(F(n)\)
\]
令 \(G(n)=\sum_{n|d}F(d)\),则
G(n)&=\sum_{n|d}F(d)\\
&=\sum_{n|d}\sum_{i=1}^m[\gcd(x,i)=d]\\
&=\sum_{i=1}^m[n|\gcd(x,i)]\\
?&=\sum_{i=1}^{\left\lfloor\frac mn\right\rfloor}\left[1|\gcd\left(\frac xn,i\right)\right]
\end{aligned}
\]
实际上这样是有问题的,因为(在后面)无法保证 \(n|x\),此时 \(G(n)\) 就一定为 \(0\) 了。
我们再多化一步:
G(n)&=\sum_{i=1}^{\left\lfloor\frac mn\right\rfloor}\left[1|\gcd\left(\frac xn,i\right)\right][n|x]\\
&=\left\lfloor\frac mn\right\rfloor\cdot[n|x]
\end{aligned}
\]
根据 \(G(n)=\sum_{n|d}F(d)\),我们反演到 \(F\),得
F(n)&=\sum_{n|d}\mu\left(\frac dn\right)G(d)\\
&=\sum_{i=1}^{\left\lfloor\frac mn\right\rfloor}\mu(i)G(ni)\\
&=\sum_{i=1}^{\left\lfloor\frac mn\right\rfloor}\mu(i)\left\lfloor\frac{m}{ni}\right\rfloor[(ni)|x]
\end{aligned}
\]
我们发现后面的布尔表达式可以当作条件。原本的条件本来就是 \(\to +\infty\) 的,只不过超过了 \(\left\lfloor\frac mn\right\rfloor\) 没有意义。因此直接把条件换成 \([(ni)|x]\) 即可。又因为 \(n|x\) 在上面的枚举过程中是成立的,同时可以转化为 \(\left[i|\frac xn\right]\)。
\]
这样的一次枚举是 \(O\left(d\left(\frac xn\right)\right)\) 的,由于 \(1\sim m\) 的约数个数和均摊是 \(O(\log m)\) 的,其中最多的有 \(128\) 个约数,但是这样的数肯定不是很多,并且其中很多被枚举到的数都是质因数,迭代一下并不会造成很大的复杂度。
然后我们需要再把状态转移方程稍微转化一下,把 \(f[i]\) 移到左边
f[i]&=1+\frac{\sum_{d|i,d<i}f[d]\times F(d)+f[i]\times F(i)}n,x=i\\
\frac{n-F(i)}{n}\cdot f[i]&=1+\frac{\sum_{d|i,d<i}f[d]\times F(d)}n,x=i\\
f[i]&=\frac{n+\sum_{d|i,d<i}f[d]\times F(d)}{n-F(i)},x=i
\end{aligned}
\]
就得到了真正的转移方程。
时间复杂度 \(O(m\log^2 m)\)。
Code:
#include<cstdio>
#include<cstring>
#include<vector>
#define ll long long
#define p 1000000007
using std::vector;
vector<int> v[100100];//约数用 vector 存一下,每次 √m 枚举不是很稳
ll qpow(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans*x%p;
x=x*x%p;
y>>=1;
}
return ans;
}
bool is[100100];
int pri[100100],mu[100100],cnt=0;
ll f[100100];
int n;
int calc(int x,int y)//1~n 中 gcd(x,i)=y 的数的个数
{
int g=x/y,ans=0;
for(int i=0;i<v[g].size();++i)
ans+=mu[v[g][i]]*(n/v[g][i]/y);
return ans;
}
int main()
{
scanf("%d",&n);
f[1]=1;
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;
}
}
}
for(int i=1;i<=n;++i)
for(int j=i;j<=n;j+=i)
v[j].push_back(i);
ll ans=1,inv=qpow(n,p-2);
for(int i=2;i<=n;++i)
{
for(int j=0;j<v[i].size()-1;++j)
f[i]=(f[i]+calc(i,v[i][j])*f[v[i][j]]%p)%p;
f[i]=(f[i]*inv+1)%p;
ll g=n-calc(i,i);
f[i]=f[i]*n%p*qpow(g,p-2)%p;
ans=(ans+f[i])%p;
}
printf("%lld\n",ans*qpow(n,p-2)%p);
return 0;
}
CF1139D Steps to One 题解【莫比乌斯反演】【枚举】【DP】的更多相关文章
- CF1139D Steps to One (莫比乌斯反演 期望dp)
\[ f[1] = 0 \] \[ f[i] = 1 + \frac{1}{m} \sum_{j = 1} ^ n f[gcd(i, j)] \ \ \ \ \ \ (i != 1) \] 然后发现后 ...
- Problem b 莫比乌斯反演+枚举除法的取值
莫比乌斯反演+枚举除法的取值 第二种形式: f(n)表示gcd(x,y)=n的数量. F(n)表示gcd(x,y)是n的倍数的数量. /** 题目:Problem b 链接:https://vjudg ...
- 【51nod1678】lyk与gcd(莫比乌斯反演+枚举因数)
点此看题面 大致题意: 一个长度为\(n\)的数组,实现两种操作:单点修改,给定\(i\)求\(\sum_{j=1}^na_j[gcd(i,j)=1]\). 莫比乌斯反演 考虑推一推询问操作的式子: ...
- 【bzoj3529】[Sdoi2014]数表 莫比乌斯反演+离线+树状数组
题目描述 有一张n×m的数表,其第i行第j列(1 <= i <= n ,1 <= j <= m)的数值为能同时整除i和j的所有自然数之和.给定a,计算数表中不大于a的数之和. ...
- 【bzoj3561】DZY Loves Math VI 莫比乌斯反演
题目描述 给定正整数n,m.求 输入 一行两个整数n,m. 输出 一个整数,为答案模1000000007后的值. 样例输入 5 4 样例输出 424 题解 莫比乌斯反演 (为了方便,以下公式默认$ ...
- 【bzoj4407】于神之怒加强版 莫比乌斯反演+线性筛
题目描述 给下N,M,K.求 输入 输入有多组数据,输入数据的第一行两个正整数T,K,代表有T组数据,K的意义如上所示,下面第二行到第T+1行,每行为两个正整数N,M,其意义如上式所示. 输出 如题 ...
- 【bzoj4816】[Sdoi2017]数字表格 莫比乌斯反演
题目描述 Doris刚刚学习了fibonacci数列.用f[i]表示数列的第i项,那么 f[0]=0 f[1]=1 f[n]=f[n-1]+f[n-2],n>=2 Doris用老师的超级计算机生 ...
- [Noi2010]能量采集 (莫比乌斯反演)
[Noi2010]能量采集 Description 栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量.在这些植物采集能量后, 栋栋再使用一个能量汇集机器把这些植物采集到的能 ...
- 【HDU1695】GCD(莫比乌斯反演)
[HDU1695]GCD(莫比乌斯反演) 题面 题目大意 求\(a<=x<=b,c<=y<=d\) 且\(gcd(x,y)=k\)的无序数对的个数 其中,你可以假定\(a=c= ...
随机推荐
- 2018年3大UI设计趋势,你知道吗?
以下内容由Mockplus团队翻译整理,仅供学习交流,Mockplus是更快更简单的原型设计工具. 之前小编已经和大家讨论了2018年软件测试的五大趋势,现在让我们一起来看看移动UI设计在2018年会 ...
- svn建立分支和svn代码合并的操作方法
首先说下为什么我们需要用到分支-合并.比如项目demo下有两个小组,svn下有一个trunk版.由于客户需求突然变化,导致项目需要做较大改动,此时项目组决定由小组1继续完成原来正进行到一半的工作[某个 ...
- 看图说话:关于BI那点事儿
[编者按]BI=DW+数据挖掘+业务分析+社会学?BI三部曲:管数据.看数据.源数据.BI有三种放法:技术部.业务部和独立部门.BI的工作=20%数据平台+30%数据支持+50%数据应用.
- JSP中的一个树型结构
看方力勋的javaWeb,采用左右值来表示树型结构(就是俺门的多级分类)表结构 页面代码 <%@ page language="java" import="java ...
- BI使用者的角色
把企业中的BI使用者的角色分成如下几类: 系统管理员:没有数据权限:没有功能权限:负责配置其他人的权限:BI专家:拥有所有数据权限:拥有多维分析,报表查看,报表开发,模型开发权限:负责开发和维护BI系 ...
- 18-10-30 Scrum Meeting 2
目录 站立式会议 工作记录 昨天完成的工作 1 主要完成了单词简单释义浏览和单词详细释义浏览的功能 并且已经测试和上传eolinker 2 3 主要搭建起爬虫的框架平台,并且测试了py连接服务器的功能 ...
- Lucene之模糊、精确、匹配、范围、多条件查询
Lucene的查询方式很 丰富,对于数值类型的数据,采取TermRangeQuery的方式,对于String类型的,就可以采取TermQuery等,查询方式了,可以通过采取合适的查询方式,检索到数据. ...
- 使用Python登录腾讯MTA数据分析平台,然后获取相关数据
思路: 第一步:使用pypeteer.launcher打开浏览器, 第二步:找到mta的登录页面,默认是使用QQ登录的,需要再触发一下切换使用帐号密码登录的按钮(通过使用iframe嵌入的腾讯单点登录 ...
- Mounting VMDK files in Linux
1.用 loop 方式挂载 vmdk 文件 losetup /dev/loop0 docker_pull-flat.vmdk 2.查看分区 [root@localhost]# parted /dev/ ...
- java—ThreadLocal模式与OSIV模式(53)
ThreadLocal: 维护线程局部的变量. ThreadLocal 不是线程.它就是一个Map.可以保存对象. 它保存的对象,只与当前线程相关. 当一个线程还没有运行完成时,如果不想传递数据,可以 ...