Luogu P1447 [NOI2010]能量采集
Preface
最近反演题做多了看什么都想反演。这道题由于数据弱,解法多种多样,这里简单分析一下。
首先转化下题目就是对于一个点\((x,y)\),所消耗的能量就是\(2(\gcd(x,y)-1)+1=2\cdot\gcd(x,y)-1\)(小学奥数题)
所以求和就是求\(\sum_{i=1}^n\sum_{j=1}^m2\cdot\gcd(i,j)-1=2\cdot\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)-nm\),因此主要问题就变成了求解\(\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)\)(对于反演来说,容斥的话直接怎么暴力怎么来)
一.转化+递推+容斥法
首先转化问题,我们考虑每一个数当它作为其它两个数的\(\gcd\)时会产生多少贡献。
先把问题简单化,我们先求出一个数为其它两个数的约数的方案数\(f(i)\),显然小学生都知道\(f(i)=\lfloor\frac{n}{i} \rfloor\lfloor\frac{m}{i} \rfloor\)
但是可能\(f(i)\)会多算当\(i|j\)时\(j\)的贡献,因此我们需要减掉\(\sum_{i|j} f(j)\)
因此我们从大到小枚举并计算\(f(i)\)即可,结合调和级数的公式发现复杂度为\(O(n(\ln(n)+H_n))\)(\(H_n\)是欧拉常数)
CODE
#include<cstdio>
#define RI register int
using namespace std;
int n,m,lim; long long ans,f[100005];
inline int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
scanf("%d%d",&n,&m); lim=min(n,m);
for (RI i=lim;i;--i)
{
f[i]=1LL*(n/i)*(m/i);
for (RI j=i<<1;j<=lim;j+=i) f[i]-=f[j];
ans+=1LL*((i<<1LL)-1)*f[i];
}
return printf("%lld",ans),0;
}
二.我的蒟蒻反演做法
反演题做的多了就算是像我这样的蒟蒻也能自己推一推了。
感觉这个很套路就那以前的套路去套结果5min就搞出来一个看上去很正确的式子然后就过了。
这里讲一讲我的做法,根据之前做的一些反演题(如Luogu P2257 YY的GCD)我们很套路的来:
令\(f(d)\)为\(\gcd(i,j)(i\in[1,n],j\in[1,m])=d\)的个数,\(F(s)\)为\(d|\gcd(i,j)\)的个数
即:
\]
\]
考虑所求,则有:
\(ans=\sum_{d=1}^{\min(n,m)}f(d)\cdot d\)
还是用莫比乌斯反演定理,得到:
\]
把\(F(s)=\lfloor\frac{n}{s} \rfloor \lfloor\frac{m}{s} \rfloor\)代进去有:
\]
一个简单的套路,把枚举\(d\)变成\(n\)是\(d\)的多少倍,即:
\]
再把这个代回去就有:
\]
还要化简么?我们看看这道题的数据范围,\(n,m\le100000\),而且是单组询问
因此我们发现这个式子的复杂度根据调和级数定理为\(O(n(\ln(n)+H_n))\),因此暴力即可过
CODE
#include<cstdio>
#define RI register int
using namespace std;
const int P=100000;
int prime[P+5],cnt,phi[P+5],n,m,lim; bool vis[P+5]; long long ans,sum[P+5];
#define Pi prime[j]
inline void Euler(void)
{
vis[1]=phi[1]=1; RI i,j; for (i=2;i<=P;++i)
{
if (!vis[i]) prime[++cnt]=i,phi[i]=i-1;
for (RI j=1;j<=cnt&&i*Pi<=P;++j)
{
vis[i*Pi]=1; if (i%Pi) phi[i*Pi]=phi[i]*(Pi-1);
else { phi[i*Pi]=phi[i]*Pi; break; }
}
}
for (i=1;i<=P;++i) sum[i]=sum[i-1]+phi[i];
}
inline int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
scanf("%d%d",&n,&m); Euler(); lim=min(n,m);
for (RI l=1,r;l<=lim;l=r+1)
{
r=min(n/(n/l),m/(m/l)); ans+=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
}
return printf("%lld",(ans<<1LL)-1LL*n*m),0;
}
三.利用狄利克雷卷积再加速
我们想一下,如果这题多组询问怎么办不保证毒瘤出题人不会出
因此我们思考应该还有更优秀的做法事实上也是有的
从第二种做法的\(f(d)=\sum_{d|s} \mu(\lfloor\frac{s}{d} \rfloor)\lfloor\frac{n}{s} \rfloor \lfloor\frac{m}{s} \rfloor\)开始,我们令\(T=ds\),则有:
\]
由于经典的狄利克雷卷积我们知道\(\mu\)的一个性质:\(\sum_{d|n}d\cdot\mu(\lfloor\frac{n}{d} \rfloor)=\phi(n)\)
(这个貌似也可以用欧拉函数的性质\(\sum_{d|n}\phi(d)=n\)再反演得来反正我不会证)
所以到现在就很简单了,把这个代进去就有:
\]
然后就是\(O(n)\)的式子了,当然,这个明显也可以做一发欧拉函数的前缀和结合除法分块做到单次询问\(O(\sqrt {\min(n,m))}\)的。因此多组询问也不再话下。
CODE
#include<cstdio>
#define RI register int
using namespace std;
const int P=100000;
int prime[P+5],cnt,phi[P+5],n,m,lim; bool vis[P+5]; long long ans,sum[P+5];
#define Pi prime[j]
inline void Euler(void)
{
vis[1]=phi[1]=1; RI i,j; for (i=2;i<=P;++i)
{
if (!vis[i]) prime[++cnt]=i,phi[i]=i-1;
for (RI j=1;j<=cnt&&i*Pi<=P;++j)
{
vis[i*Pi]=1; if (i%Pi) phi[i*Pi]=phi[i]*(Pi-1);
else { phi[i*Pi]=phi[i]*Pi; break; }
}
}
for (i=1;i<=P;++i) sum[i]=sum[i-1]+phi[i];
}
inline int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
scanf("%d%d",&n,&m); Euler(); lim=min(n,m);
for (RI l=1,r;l<=lim;l=r+1)
{
r=min(n/(n/l),m/(m/l)); ans+=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
}
return printf("%lld",(ans<<1LL)-1LL*n*m),0;
}
Postscript
这真是一道很不错的数学题,上面的三种做法感觉都可以。
然而理论复杂度最优的算法三跑的和算法二一样。估计是除法做的太多常数偏大
而算法一就很优秀了,常数极小且不用欧拉筛的预处理,因此再速度上碾压了其它两种解法。
而且暴力不动脑的推导过程,啧啧称奇。
Luogu P1447 [NOI2010]能量采集的更多相关文章
- Luogu P1447 [NOI2010]能量采集 数论??欧拉
刚学的欧拉反演(在最后)就用上了,挺好$qwq$ 题意:求$\sum_{i=1}^{N}\sum_{j=1}^{M}(2*gcd(i,j)-1)$ 原式 $=2*\sum_{i=1}^{N}\sum_ ...
- luogu P1447 [NOI2010]能量采集 欧拉反演
题面 题目要我们求的东西可以化为: \[\sum_{i=1}^{n}\sum_{j=1}^{m}2*gcd(i,j)-1\] \[-nm+2\sum_{i=1}^{n}\sum_{j=1}^{m}gc ...
- 洛谷P1447 - [NOI2010]能量采集
Portal Description 给出\(n,m(n,m\leq10^5),\)计算\[ \sum_{i=1}^n \sum_{j=1}^m (2gcd(i,j)-1)\] Solution 简单 ...
- P1447 [NOI2010]能量采集
题目描述 栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量.在这些植物采集能量后,栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起. 栋栋的植物种得非常整齐,一共 ...
- 洛谷 P2158 [SDOI2008]仪仗队 && 洛谷 P1447 [NOI2010]能量采集
https://www.luogu.org/problemnew/show/P2158 以人所在位置为(0,0)建立坐标系, 显然除了(0,1)和(1,0)外,可以只在坐标(x,y)的gcd(x,y) ...
- 洛谷P1447 [NOI2010]能量采集(容斥)
传送门 很明显题目要求的东西可以写成$\sum_{i=1}^{n}\sum_{j=1}^m gcd(i,j)*2-1$(一点都不明显) 如果直接枚举肯定爆炸 那么我们设$f[i]$表示存在公因数$i$ ...
- 洛谷 P1447 [NOI2010]能量采集 (莫比乌斯反演)
题意:问题可以转化成求$\sum_{i=1}^{n}\sum_{j=1}^{m}(2*gcd(i,j)-1)$ 将2和-1提出来可以得到:$2*\sum_{i=1}^{n}\sum_{j=1}^{m} ...
- [NOI2010]能量采集(莫比乌斯反演)
题面: bzoj luogu NOI2010能量采集 题解 读完题之后我们发现在每个产生贡献的点\((x1,y1)\)中,它与原点之间的点\((x2,y2)\)都满足\(x2|x1\),\(y2|y1 ...
- BZOJ 2005: [Noi2010]能量采集
2005: [Noi2010]能量采集 Time Limit: 10 Sec Memory Limit: 552 MBSubmit: 3312 Solved: 1971[Submit][Statu ...
随机推荐
- Android Context类
一.Context类的使用场景 ①.创建Application对象时,而且整个app公用一个Application对象 ②.创建Service对象时 ③.创建Activity对象时 二.应用程序共有几 ...
- 使用wxpy来实现自动发送消息统计微信好友信息的功能
发送消息太频繁会出现禁言消息 1:导入wxpy模块 pip install wxpy pip3 install wxpy #二者选一 调用模块 # 导入模块 from wxpy import * # ...
- 心迹 使用说明&功能展示
下载地址 心迹.apk 更新于2018.8.9 11:47 测试账号:用户名testing,密码testing 项目地址 GitHub 注册&登录 第一次使用心迹app时,必须进行注册,以便区 ...
- Centos7安装搭建Bugzilla 5.0
1.安装准备: Centos7保证网络连通,如果网络不能连通,可通过配置yum源使用代理服务. vim /etc/yum.conf # The proxy server - proxy server: ...
- 11-OpenLDAP主从同步
OpenLDAP主从同步 阅读视图 部署环境 OpenLDAP服务器初始化 配置主服务器同步策略 配置从服务器 OpenLDAP主从同步验证 故障诊断 1. 部署环境 本文以两台服务器为蓝本演示其同步 ...
- 洗礼灵魂,修炼python(55)--爬虫篇—知识补充—RFC 2616 http状态码
不多说直接上状态码表: 状态码 含义 100 客户端应当继续发送请求.这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝.客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽 ...
- 使用EWS API读取邮件
#安装EwsManagedApi.msi,然后导入EWS模块 Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Ser ...
- PHP 截取字符串乱码的解决方案
今天遇到一个坑,左右调试坑的我一脸懵逼,当我们对一条字符串进行截取的时候,通常第一个想到的就是substr()函数了,但是如果是中文+数字的字符串的话,这时候使用substr进行截取就会出现乱码的问题 ...
- Hive-1.2.1_05_案例操作
1. 建库建表 # 建库 create database exercise; # 建表 create table student(Sno int,Sname string,Sex string,Sag ...
- IntelliJ IDEA src下新建包, 没有层级结构
新建项目后再src先右键点击新建包 com.example , 然后想在com.example 包中包含其他包, 当点击src新建包后,出现如图的情况 解决: 继续在src上右键新建package ...