[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)
题目描述
设d(x)d(x)d(x)为xxx的约数个数,给定NNN、MMM,求 ∑i=1N∑j=1Md(ij)\sum^{N}_{i=1}\sum^{M}_{j=1} d(ij)i=1∑Nj=1∑Md(ij)
N,M,T<=50000N,M,T<=50000N,M,T<=50000
题目分析
首先很不显然的有这样一个结论:
d(ij)=∑x∣i∑y∣j[(x,y)==1]d(ij)=\sum_{x|i}\sum_{y|j}[(x,y)==1]d(ij)=x∣i∑y∣j∑[(x,y)==1]
证明如下
将一个数唯一质因数分解为p1k1p2k2p3k3...pnknp_1^{k_1}p_2^{k_2}p_3^{k_3}...p_n^{k_n}p1k1p2k2p3k3...pnkn,其约数个数为(k1+1)(k2+1)(k3+1)...(kn+1)(k_1+1)(k_2+1)(k_3+1)...(k_n+1)(k1+1)(k2+1)(k3+1)...(kn+1)
考虑一个质数ppp对d(ij)d(ij)d(ij)的影响,假设iii分解质因数后有kkk个ppp,jjj分解质因数后有qqq个ppp,则产生的贡献即为k+q+1k+q+1k+q+1,接下来是关键的一步(反正我想不到XD)
k+q+1=∑x=0k∑y=0q[(px,py)==1]k+q+1=\sum_{x=0}^k\sum_{y=0}^q[(p^x,p^y)==1]k+q+1=x=0∑ky=0∑q[(px,py)==1]
(此句可自行忽略:只有当xy=0xy=0xy=0时才会有值,这就相当于一个(k+1)∗(q+1)(k+1)∗(q+1)(k+1)∗(q+1)的矩形,只取了左下角的LLL字型的一块)
根据乘法原理,有
d(ij)=∑x1=0k1∑y1=0q1[(p1x1,p1y1)==1]∗∑x2=0k2∑y2=0q2[(p2x2,p2y2)==1]⋅⋅⋅∗∑xn=0kn∑yn=0qn[(pnxn,pnyn)==1]
d(ij)=\sum_{x_1=0}^{k_1}\sum_{y_1=0}^{q_1}[(p_1^{x_1},p_1^{y_1})==1]\newline
*\sum_{x_2=0}^{k_2}\sum_{y_2=0}^{q_2}[(p_2^{x_2},p_2^{y_2})==1]\newline
···\newline
*\sum_{x_n=0}^{k_n}\sum_{y_n=0}^{q_n}[(p_n^{x_n},p_n^{y_n})==1]
d(ij)=x1=0∑k1y1=0∑q1[(p1x1,p1y1)==1]∗x2=0∑k2y2=0∑q2[(p2x2,p2y2)==1]⋅⋅⋅∗xn=0∑knyn=0∑qn[(pnxn,pnyn)==1]
因为必须满足所有(pnxn,pnyn)==1(p_n^{x_n},p_n^{y_n})==1(pnxn,pnyn)==1才会对答案造成贡献,则可以变形为(∏i=1npnxn,∏i=1npnyn)==1(\prod_{i=1}^np_n^{x_n},\prod_{i=1}^np_n^{y_n})==1(i=1∏npnxn,i=1∏npnyn)==1
所以d(ij)=∑x∣i∑y∣j[(x,y)==1]d(ij)=\sum_{x|i}\sum_{y|j}[(x,y)==1]d(ij)=x∣i∑y∣j∑[(x,y)==1]
(以上证明摘自:传送门)
证毕
现在就有了Ans=∑i=1N∑j=1M∑x∣i∑y∣j[(x,y)==1]Ans=\sum_{i=1}^{N}\sum_{j=1}^{M}\sum_{x|i}^{}\sum_{y|j}^{}[(x,y)==1]Ans=i=1∑Nj=1∑Mx∣i∑y∣j∑[(x,y)==1]
根据数论中切换枚举次序的套路以及莫比乌斯反演对布尔条件框的替换,我们得到
Ans=∑x=1N∑y=1M⌊Nx⌋⌊My⌋[(x,y)==1]=∑x=1N∑y=1M⌊Nx⌋⌊My⌋∑k∣(x,y)μ(k)=∑k=1min(N,M)μ(k)∑k∣x⌊Nx⌋∑k∣y⌊My⌋=∑k=1min(N,M)μ(k)∑x=1⌊Nk⌋⌊Nkx⌋∑y=1⌊Mk⌋⌊Mky⌋
Ans=\sum_{x=1}^{N}\sum_{y=1}^{M}⌊\frac{N}{x}⌋⌊\frac{M}{y}⌋[(x,y)==1]\newline
=\sum_{x=1}^{N}\sum_{y=1}^{M}⌊\frac{N}{x}⌋⌊\frac{M}{y}⌋\sum_{k|(x,y)}\mu(k)\newline
=\sum_{k=1}^{min(N,M)}\mu(k)\sum_{k|x}^{}⌊\frac{N}{x}⌋\sum_{k|y}^{}⌊\frac{M}{y}⌋\newline
=\sum_{k=1}^{min(N,M)}\mu(k)\sum_{x=1}^{⌊\frac{N}{k}⌋}⌊\frac{N}{kx}⌋\sum_{y=1}^{⌊\frac{M}{k}⌋}⌊\frac{M}{ky}⌋
Ans=x=1∑Ny=1∑M⌊xN⌋⌊yM⌋[(x,y)==1]=x=1∑Ny=1∑M⌊xN⌋⌊yM⌋k∣(x,y)∑μ(k)=k=1∑min(N,M)μ(k)k∣x∑⌊xN⌋k∣y∑⌊yM⌋=k=1∑min(N,M)μ(k)x=1∑⌊kN⌋⌊kxN⌋y=1∑⌊kM⌋⌊kyM⌋
于是这里使用分块优化,预处理μ\muμ的前缀和
最后考虑后面的两个Sigma怎么处理
可以观察发现它们都是∑i=1n⌊ni⌋\sum_{i=1}^{n}⌊\frac{n}{i}⌋∑i=1n⌊in⌋的形式,此处可以用分块优化Θ(nn)\Theta(n\sqrt{n})Θ(nn)预处理
其实还可以Θ(n)\Theta(n)Θ(n)预处理,可以发现∑i=1n⌊ni⌋=∑i=1n∑i∣dn1\sum_{i=1}^{n}⌊\frac{n}{i}⌋=\sum_{i=1}^{n}\sum_{i|d}^{n}1∑i=1n⌊in⌋=∑i=1n∑i∣dn1
显然是约数个数和d(n)d(n)d(n)的前缀和函数,于是线性筛出μ(n)\mu(n)μ(n)与d(n)d(n)d(n),求出前缀和即可
每次询问Θ(n)\Theta(\sqrt{n})Θ(n), 预处理Θ(n)\Theta(n)Θ(n),总时间复杂度为Θ(nn)\Theta(n\sqrt{n})Θ(nn)
TIPS
线性筛约数个数时存一下最小的质数p1p_1p1的次方+1+1+1,即存下(k1+1)(k_1+1)(k1+1)就可以方便的线性筛了
线性筛约数和同理,存一下最小的质数p1p_1p1所对应的首项为111,公比为p1p_1p1,项数为k1+1k_1+1k1+1的等比数列,即存下∑i=1k1p1i\sum_{i=1}^{k_1}p_1^i∑i=1k1p1i就可以愉快的线性筛了
AC code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 100001;
namespace Mobius
{
#define LL long long
int Prime[MAXN], Cnt, mu[MAXN], d[MAXN], Minseq[MAXN];
bool IsnotPrime[MAXN];
LL sum_MU[MAXN], sum_D[MAXN];
void init()
{
mu[1] = d[1] = Minseq[1] = 1;
for(int i = 2; i < MAXN; ++i)
{
if(!IsnotPrime[i])
Prime[++Cnt] = i, mu[i] = -1, d[i] = Minseq[i] = 2;
for(int j = 1; j <= Cnt && i * Prime[j] < MAXN; ++j)
{
IsnotPrime[i * Prime[j]] = 1;
if(i % Prime[j] == 0)
{
Minseq[i * Prime[j]] = Minseq[i]+1;
mu[i * Prime[j]] = 0;
d[i * Prime[j]] = d[i] / Minseq[i] * Minseq[i * Prime[j]];
break;
}
Minseq[i * Prime[j]] = 2;
mu[i * Prime[j]] = -mu[i];
d[i * Prime[j]] = d[i]<<1;
}
}
for(int i = 1; i < MAXN; ++i)
sum_MU[i] = sum_MU[i-1] + mu[i],
sum_D[i] = sum_D[i-1] + d[i];
}
LL calc(int N, int M)
{
if(N > M) swap(N, M);
LL ret = 0;
for(int i = 1, j; i <= N; i=j+1)
{
j = min(N/(N/i), M/(M/i));
ret += (sum_MU[j]-sum_MU[i-1]) * sum_D[N/i] * sum_D[M/i];
}
return ret;
}
}
using namespace Mobius;
int main ()
{
init();
int T, n, m;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
printf("%lld\n", calc(n, m));
}
}
[SDOI2015][bzoj 3994][Luogu P3327] 约数个数和 (莫比乌斯反演)的更多相关文章
- P3327 [SDOI2015]约数个数和 莫比乌斯反演
P3327 [SDOI2015]约数个数和 莫比乌斯反演 链接 luogu 思路 第一个式子我也不会,luogu有个证明,自己感悟吧. \[d(ij)=\sum\limits_{x|i}\sum\li ...
- BZOJ 3994: [SDOI2015]约数个数和 [莫比乌斯反演 转化]
2015 题意:\(d(i)\)为i的约数个数,求\(\sum\limits_{i=1}^n \sum\limits_{j=1}^m d(ij)\) \(ij\)都爆int了.... 一开始想容斥一下 ...
- luogu P3327 [SDOI2015]约数个数和 莫比乌斯反演
题面 我的做法基于以下两个公式: \[[n=1]=\sum_{d|n}\mu(d)\] \[\sigma_0(i*j)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\] 其中\(\ ...
- BZOJ 3994: [SDOI2015]约数个数和3994: [SDOI2015]约数个数和 莫比乌斯反演
https://www.lydsy.com/JudgeOnline/problem.php?id=3994 https://blog.csdn.net/qq_36808030/article/deta ...
- [BZOI 3994] [SDOI2015]约数个数和(莫比乌斯反演+数论分块)
[BZOI 3994] [SDOI2015]约数个数和 题面 设d(x)为x的约数个数,给定N.M,求\(\sum _{i=1}^n \sum_{i=1}^m d(i \times j)\) T组询问 ...
- 洛谷P3327 [SDOI2015]约数个数和(莫比乌斯反演)
题目描述 设d(x)为x的约数个数,给定N.M,求 \sum^N_{i=1}\sum^M_{j=1}d(ij)∑i=1N∑j=1Md(ij) 输入输出格式 输入格式: 输入文件包含多组测试数据.第 ...
- 【BZOJ3994】[SDOI2015]约数个数和 莫比乌斯反演
[BZOJ3994][SDOI2015]约数个数和 Description 设d(x)为x的约数个数,给定N.M,求 Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组 ...
- [SDOI2015][bzoj3994] 约数个数和 [莫比乌斯反演]
题面: 传送门 思路: 首先,我们需要证明一个结论:d(i*j)等于sigma(gcd(x,y)==1),其中x为i的约数,y为j的约数 对于nm的每一个质因子pi分别考虑,设n = pi^ai + ...
- BZOJ3994: [SDOI2015]约数个数和(莫比乌斯反演)
Description 设d(x)为x的约数个数,给定N.M,求 Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组数. 接下来的T行,每行两个整数N.M. Out ...
随机推荐
- MySQL引擎类型(三)
InnoDB: 1)经常更新的表,适合处理多重并发的更新请求. 2)支持事务. 3)可以从灾难中恢复(通过bin-log日志等). 4)外键约束.只有他支持外键. 5)支持自动增加列属性auto_in ...
- Python循环的基本使用(for in、while)
Python的循环有两种: 一种是for-in 循环:主要用于遍历tuple.list; 一种是while循环:只要条件满足,就不断循环,条件不满足时退出循环. #!/usr/bin/python # ...
- 剑指offer11:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。(进制转换,补码反码)
1. 题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 2. 思路和方法 使用移位(<<)和 “| & !”操作来实现.1的二进制是:前面都是0,最后一位 ...
- python + pyinstaller 实现将python程序打包成exe文件直接运行
pyinstaller 我们在平常学习使用python的时候经常会自己编写一些小程序来使用,虽然python是跨平台的语言,但是如果我们想要在一个没有python以及很多库环境的电脑上使用我们的小程序 ...
- 20191031:GIL全局解释锁
20191031:GIL全局解释锁 总结关于GIL全局解释锁的个人理解 GIl全局解释锁,本身不是Python语言的特性,而是Python语言底层的c Python解释器的一个特性.在其他解释器中是没 ...
- errgroup 分析
errgroup 在 WaitGroup 的基础上实现子协程错误传递, 同时使用 context 控制协程的生命周期. 使用 errgroup 的使用非常简单 package main import ...
- Scratch编程:初识Scratch及编程工具安装(一)
“ Scratch是一款由美国麻省理工学院(MIT)设计开发的少儿编程工具.” Scratch采用可视化.模块化的编程方式,非常适合青少年作为初次接触编程的工具和语言来学习,进而用其编写充满趣味的小程 ...
- 微信小程序的页面跳转==编程式导航传参 和 标签的方法传参==以及如何过去传递过来的参数
小程序导航传参接收传递过来的参数 在onload中 实例
- Luogu5285 [十二省联考2019] 骗分过样例
题目分析: 观察前3个点,$361=19*19$,所以可以发现实际上就是快速幂,然后模数猜测是$998244353$,因为功能编号里面有这个数字,用费马小定理处理一下. $pts:12$ 观察第4个点 ...
- webpack 3.1 升级webpack 4.0
webpack 3.1 升级webpack 4.0 为了提升打包速度以及跟上主流技术步伐,前段时间把项目的webpack 升级到4.0版本以上 webpack 官网:https://webpack.j ...