题目大意:

给定\(a, b, c\),求\(\sum \limits_{i = 1}^a \sum \limits_{j = 1}^b \sum \limits_{k = 1}^c [(i, j) = 1][(j, k) = 1][(i, k) = 1]\)

$a, b, c \leq 5*10^4 $


首先莫比乌斯反演

$Ans = \sum \limits_{i = 1}^a \sum \limits_{j = 1}^b \sum \limits_{k = 1}^c [(i, j) = 1][(j, k) = 1][(i, k) = 1] $

\(= \sum \limits_{i} \sum \limits_{j} \sum \limits_{k} \sum \limits_{x |i \;x|j} \mu(x) \sum \limits_{y|j\;y|k} \mu(y) \sum \limits_{z |i\;z|k} \mu(z)\)

\(= \sum \limits_{x} \sum \limits_{y} \sum \limits_{z} \mu(x) \mu(y) \mu(z) \frac{A}{lcm(x, y)} \frac{B}{lcm(x, z)} \frac{C}{lcm(y, z)}\)

那么考虑计算这个式子

注意到其实有效的三元组\((x, y, z)\)是十分稀少的

我们考虑用一种高效的办法来找到所有的三元组

三元环计数是一个十分便利的算法

如果\(\mu(u), \mu(v) \neq 0, lca(u, v) \leq C\),那么我们连边\((u, v)\)

怎么连边呢?

我们先枚举\(lca(u, v)\),然后枚举\(u\),之后再枚举\(gcd(u, v)\)判断即可

对于有两个数相同的情况和三个数相同的情况进行特判即可

复杂度不会算,反正跑的挺快的

ps:我怎么感觉dfs也能过呢?


#include <bits/stdc++.h>
using namespace std; #define mp make_pair
#define pii pair <int, int> #define ri register int
#define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
#define drep(io, ed, st) for(ri io = ed; io >= st; io --) const int sid = 5e4 + 5;
const int cid = 2e6 + 5;
const int mod = 1e9 + 7; inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; }
inline int mul(int a, int b) { return 1ll * a * b % mod; } int a, b, c, id, ans, tot;
int mu[sid], pr[sid], nop[sid];
int eu[cid], ev[cid], ew[cid], d[sid], vis[sid], vv[sid];
vector <pii> go[sid];
vector <int> fac[sid]; inline void Init() {
mu[1] = 1;
for (int i = 2; i <= 50000; i ++) {
if (!nop[i]) { pr[++ tot] = i; mu[i] = mod - 1; }
for (int j = 1; j <= tot; j ++) {
int p = i * pr[j];
if(p > 50000) break; nop[p] = 1;
if(i % pr[j] == 0) break; if(mu[i]) mu[p] = mod - mu[i];
}
} for (ri i = 1; i <= tot; i ++)
for (ri j = pr[i]; j <= 50000; j += pr[i])
fac[j].push_back(pr[i]);
} inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
inline int lcm(int a, int b) { return 1ll * a * b / gcd(a, b);} inline void calc() {
if(c < a) swap(a, c);
if(c < b) swap(b, c);
if(b < a) swap(a, b); for (ri x = 1; x <= a; x ++) // x = y = z
if(mu[x]) inc(ans, mul(mu[x], 1ll * (a / x) * (b / x) * (c / x) % mod)); for (ri L = 1; L <= c; L ++) if(mu[L]) {
int v = fac[L].size();
for (ri S = 0; S <= (1 << v) - 1; S ++) {
int x = 1;
rep(i, 0, v - 1) if(S & (1 << i)) x *= fac[L][i];
if(x > b) continue;
for (ri T = S & (S - 1); ; T = (T - 1) & S) {
int D = 1;
rep(j, 0, v - 1) if(T & (1 << j)) D *= fac[L][j];
int y = 1ll * L * D / x;
if(x > y && y <= a) {
d[x] ++; d[y] ++;
eu[++ id] = x; ev[id] = y; ew[id] = L;
inc(ans, mul(mu[y], 1ll * (a / L) * (b / L) * (c / x) % mod));
inc(ans, mul(mu[y], 1ll * (a / x) * (b / L) * (c / L) % mod));
inc(ans, mul(mu[y], 1ll * (a / L) * (b / x) * (c / L) % mod));
inc(ans, mul(mu[x], 1ll * (a / L) * (b / L) * (c / y) % mod));
inc(ans, mul(mu[x], 1ll * (a / y) * (b / L) * (c / L) % mod));
inc(ans, mul(mu[x], 1ll * (a / L) * (b / y) * (c / L) % mod));
}
if(!T) break;
}
}
} for (ri i = 1; i <= id; i ++) {
int u = eu[i], v = ev[i];
if(d[u] > d[v]) swap(u, v);
go[u].push_back(mp(v, ew[i]));
} for (ri x = 1; x <= b; x ++) {
for (auto Y : go[x]) vis[Y.first] = x, vv[Y.first] = Y.second;
for (auto Y : go[x]) for (auto Z : go[Y.first]) if(vis[Z.first] == x) {
static int res = 0, cer = 0;
int y = Y.first, z = Z.first, xy = Y.second, yz = Z.second, xz = vv[z];
res = 0; cer = mul(mu[x], mul(mu[y], mu[z]));
inc(res, 1ll * (a / xy) * ((b / xz) * (c / yz) + (b / yz) * (c / xz)) % mod);
inc(res, 1ll * (b / xy) * ((a / xz) * (c / yz) + (a / yz) * (c / xz)) % mod);
inc(res, 1ll * (c / xy) * ((a / xz) * (b / yz) + (a / yz) * (b / xz)) % mod);
inc(ans, mul(cer, res));
}
} cout << ans << endl;
} int main() {
cin >> a >> b >> c;
Init(); calc();
return 0;
}

loj#6076「2017 山东一轮集训 Day6」三元组 莫比乌斯反演 + 三元环计数的更多相关文章

  1. LOJ #6074. 「2017 山东一轮集训 Day6」子序列

    #6074. 「2017 山东一轮集训 Day6」子序列 链接 分析: 首先设f[i][j]为到第i个点,结尾字符是j的方案数,这个j一定是从i往前走,第一个出现的j,因为这个j可以代替掉前面所有j. ...

  2. loj#6074. 「2017 山东一轮集训 Day6」子序列(矩阵乘法 dp)

    题意 题目链接 Sol 设\(f[i][j]\)表示前\(i\)个位置中,以\(j\)为结尾的方案数. 转移的时候判断一下\(j\)是否和当前位置相同 然后发现可以用矩阵优化,可以分别求出前缀积和逆矩 ...

  3. LOJ#6075. 「2017 山东一轮集训 Day6」重建

    题目描述: 给定一个 n个点m 条边的带权无向连通图 ,以及一个大小为k 的关键点集合S .有个人要从点s走到点t,现在可以对所有边加上一个非负整数a,问最大的a,使得加上a后,满足:s到t的最短路长 ...

  4. Loj #6069. 「2017 山东一轮集训 Day4」塔

    Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...

  5. Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...

  6. Loj 6068. 「2017 山东一轮集训 Day4」棋盘

    Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...

  7. loj #6077. 「2017 山东一轮集训 Day7」逆序对

    #6077. 「2017 山东一轮集训 Day7」逆序对   题目描述 给定 n,k n, kn,k,请求出长度为 n nn 的逆序对数恰好为 k kk 的排列的个数.答案对 109+7 10 ^ 9 ...

  8. LOJ #6119. 「2017 山东二轮集训 Day7」国王

    Description 在某个神奇的大陆上,有一个国家,这片大陆的所有城市间的道路网可以看做是一棵树,每个城市要么是工业城市,要么是农业城市,这个国家的人认为一条路径是 exciting 的,当且仅当 ...

  9. loj#6073. 「2017 山东一轮集训 Day5」距离(树链剖分 主席树)

    题意 题目链接 Sol 首先对询问差分一下,我们就只需要统计\(u, v, lca(u, v), fa[lca(u, v)]\)到根的路径的贡献. 再把每个点与\(k\)的lca的距离差分一下,则只需 ...

随机推荐

  1. 解决 ionic 中的 CORS(跨域)

    译者注:本人翻译功力有限,所以文中难免有翻译不准确的地方,凑合看吧,牛逼的话你看英文版的去,完事儿欢迎回来指正交流(^_^) 如果你通过 ionic serve 或者 ionic run 命令使用或 ...

  2. matplotlib 画图

    matplotlib 画图 1. 画曲线图       Tompson = np.array([0, 0, 0, 0, 0.011, 0.051, 0.15, 0.251, 0.35, 0.44, 0 ...

  3. notepad++64位添加plugin manager

    - 64位的notepad++,下载下来似乎没有plugin manager,如果真没有可以下载plugin manager. - plugin manager的下载地址:https://github ...

  4. centos6.5环境利用scp实现自动化文件备份

    centos6.5环境利用scp自动上传备份文件到指定服务器中 需要备份的主机 192.168.3.17 存放备份的主机 192.168.3.18 目的:将3.17主机上/data/storage的文 ...

  5. 转载:Linux操作系统(1.3.1)《深入理解Nginx》(陶辉)

    原文:https://book.2cto.com/201304/19611.html 1.3 准备工作 由于Linux具有免费.使用广泛.商业支持越来越完善等特点,本书将主要针对Linux上运行的Ng ...

  6. Account的简单架构

    前几天,有园友私下问我,博客中的AccountDemo后端架构为什么是那样的,是不是分层太多太冗余,故这里简单介绍下.先看解决方案工程截图: 每个工程的含义,见https://www.cnblogs. ...

  7. STM32应用实例六:与MS5837压力传感器的I2C通讯

    MS5837压力传感器是一种可用于电路板上,适用于检测10-1200mbar压力范围的传感器,灵敏度非常高,理论上能够检测到0.01mbar的压力变化,实际使用过程中测试并无明显的变化. MS5837 ...

  8. Linux 快速删除已输入的命令

    从输入模式到命令模式: 按”:“到最后一行,再按ctrl+z 就好了 history 显示命令历史列表 ↑(Ctrl+p) 显示上一条命令 ↓(Ctrl+n) 显示下一条命令 !num 执行命令历史列 ...

  9. 关于java中Stream理解

    关于java中Stream理解 Stream是什么 Stream:Java 8新增的接口,Stream可以认为是一个高级版本的Iterator.它代表着数据流,流中的数据元素的数量可以是有限的, 也可 ...

  10. PHP实现字符串转义和还原

    首先大家可以简单了解下什么是转义字符?有什么用? 转义字符是一种特殊的字符常量.转义字符以反斜线"\"开头,后跟一个或几个字符.转义字符具有特定的含义,不同于字符原有的意义,故称“ ...