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 ...
随机推荐
- ThreadLocal终极源码剖析-一篇足矣!
本文较深入的分析了ThreadLocal和InheritableThreadLocal,从4个方向去分析:源码注释.源码剖析.功能测试.应用场景. 一.ThreadLocal 我们使用ThreadLo ...
- Collections工具类
Collections 是一个操作 Set.List 和 Map 等集合的工具类. Collections 中提供了一系列静态的方法对集合元素进行排序.查询和修改等操作,还提供了对集合对象设置不可变. ...
- asp.net webapi 自定义身份验证
/// <summary> /// 验证 /// </summary> /// Account API账号 /// TimeStamp 请求时间 /// Sign 所有请求参数 ...
- javascript打印1-100内的质数
<script> /* 质数定义: 质数(prime number)又称素数,有无限个. 质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数. 1不是质数 */ //声明变量 ...
- 阵列卡raid H730写策略write-through和write-back配置说明
问题描述: 最近公司新进了测试服务器,但是在做阵列的时候忘记写策略里面的配置意思了 就网上查了一下,然后顺便做个笔记记录一下 write-through 数据在写入存储的同时,要写入缓存,这种方式安全 ...
- SVN服务端VisualSVN数据转移说明
两台服务器,进行SVN的迁移: 系统平台:windows server 2008 and windows server 2012 版本库:meishu 源服务器:192.168.0.245 目标服务器 ...
- January 10th, 2018 Week 02nd Wednesday
No need to have a reason to love you. Anything can be a reason not to love you. 喜欢你,不需要什么理由:不喜欢你,什么都 ...
- 17秋 软件工程 团队第五次作业 Alpha Scrum2
17秋 软件工程 团队第五次作业 Alpha Scrum2 今日完成的任务 杰麟:Java后端的学习: 世强:登录和注册接口编写: 港晨:完成数据库表的设计: 树民.陈翔:完成超级管理员后端框架. 其 ...
- 使用pymysql(使用一)
创建数据表 import pymysql db = pymysql.connect("localhost","root",""," ...
- centos7下安装docker(2镜像)
docker最小的镜像——hello-world 下载镜像 docker pull docker pull hello-world 查看镜像 docker images docker images ...