整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


P4619 [SDOI2018]旧试题(莫比乌斯反演,三元环计数)

Problem

计算:

∑i=1A∑j=1B∑k=1Cd(ijk)mod(109+7)\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{k=1}^{C}d(ijk) \mod (10^9+7)i=1∑A​j=1∑B​k=1∑C​d(ijk)mod(109+7)

其中 d(ijk)d(ijk)d(ijk) 表示 i×j×ki×j×ki×j×k 的约数个数

1≤T≤10,1≤A,B,C≤105,1≤∑max⁡(A,B,C)≤2×1051 ≤ T ≤ 10, 1 ≤ A, B, C ≤ 10^5, 1 ≤ \sum{\max(A, B, C)} ≤ 2 \times 10^51≤T≤10,1≤A,B,C≤105,1≤∑max(A,B,C)≤2×105

Solution

首先,我们先来一波经典反演:

第一步的结论证明:SDOI2015 约数个数和(莫比乌斯反演经典、双上限整除分块)

好耶,基础的反演到此结束,接下来才是重头戏。

首先我们显然可以根据输入的 A,B,CA,B,CA,B,C 在 O(nlogn)O(nlogn)O(nlogn) 下预处理出来所有可能需要用到的 f(A,lcm(d1,d3)⌋),f(B,lcm(d2,d1)),f(C,lcm(d2,d3))f(A,{\text{lcm}(d_1,d_3)}\rfloor),f(B, {\text{lcm}(d_2,d_1)}),f(C, {\text{lcm}(d_2,d_3)})f(A,lcm(d1​,d3​)⌋),f(B,lcm(d2​,d1​)),f(C,lcm(d2​,d3​))

然后发现我们反演的出来的这个式子直接去计算的话需要 O(n3)O(n^3)O(n3) 三重枚举,根本没法计算。

考虑优化。

显然如果我们直接 O(n3)O(n^3)O(n3) 三重枚举的话,会枚举到很多权值为 000 的 d1,d2,d3d_1,d_2,d_3d1​,d2​,d3​,即无贡献的无效枚举。

显然无效的贡献有:

  • 枚举到的 d1,d2,d3d_1,d_2,d_3d1​,d2​,d3​ 不合法
    即lcm(d1,d2)>A⇒⌊Alcm(d1,d2)⌋=0\text{lcm}(d_1,d_2)>A\Rightarrow \lfloor\cfrac{A}{\text{lcm}(d_1,d_2)}\rfloor=0lcm(d1​,d2​)>A⇒⌊lcm(d1​,d2​)A​⌋=0

  • μ(d1)=0∣∣μ(d2)=0∣∣μ(d3)=0\mu(d_1)=0 \mid\mid\mu(d_2)=0\mid\mid\mu(d_3)=0μ(d1​)=0∣∣μ(d2​)=0∣∣μ(d3​)=0

我们顺着这个思路去走,我们能不能只枚举合法的 d1,d2,d3d_1,d_2,d_3d1​,d2​,d3​,即同时满足 μ(d1)≠0\mu(d_1)\neq0μ(d1​)​=0,μ(d2)≠0\mu(d_2)\neq0μ(d2​)​=0,μ(d3)=0\mu(d_3)=0μ(d3​)=0,lcm(d1,d2)≤max⁡(A,B,C)\text{lcm}(d_1,d_2)\le\max(A, B,C)lcm(d1​,d2​)≤max(A,B,C),lcm(d2,d3)≤max⁡(A,B,C)\text{lcm}(d_2,d_3)\le\max(A, B,C)lcm(d2​,d3​)≤max(A,B,C),lcm(d1,d3)≤max⁡(A,B,C)\text{lcm}(d_1,d_3)\le\max(A, B,C)lcm(d1​,d3​)≤max(A,B,C)

所以我们进行大胆的尝试:建图减少无效的枚举!

将任意两个合法的点 u,vu,vu,v (其中 u,v∈[1,max⁡(A,B,C)]u,v\in[1,\max(A,B,C)]u,v∈[1,max(A,B,C)])连边!

即任意两个合法的点 u,vu,vu,v:μ(u)≠0\mu(u)\neq0μ(u)​=0,μ(v)≠0\mu(v)\neq0μ(v)​=0,lcm(u,v)≤max⁡(A,B,C)\text{lcm}(u,v)\le\max(A, B,C)lcm(u,v)≤max(A,B,C),考虑将他们连边,边权为 valu,v=lcm(u,v)val_{u,v}=\text{lcm}(u,v)valu,v​=lcm(u,v),那么我们可以发现,如果存在三个点 u,v,wu,v,wu,v,w 均合法,且u,v,wu,v,wu,v,w 三点有连边,即在图中形成了一个三元环的话,那么他们就会对答案产生有效贡献!贡献为

μ(u)μ(v)μ(w)×f(A,valu,v)f(B,valv,w)f(C,valu,w)\mu(u)\mu(v)\mu(w)\times f(A,{val_{u,v}})f(B,{val_{v,w}})f(C,{val_{u,w}})μ(u)μ(v)μ(w)×f(A,valu,v​)f(B,valv,w​)f(C,valu,w​)

因此,我们只需要建图,然后可以在 O(mm)O(m\sqrt{m})O(mm​) 的时间复杂度下计算出图中三元环的个数,即可计算出最终的答案!( mmm 为图的边数)

关于 O(mm)O(m\sqrt{m})O(mm​) 的三元环计数,可以参考这篇文章:不常用的黑科技——「三元环」

那么如何建图呢?我们可以把所有 μ(x)=0\mu(x)=0μ(x)=0 的数删掉,这样点数就变少了,但是现在对新点集进行暴力建图大概还是 O(n2)O(n^2)O(n2) 的复杂度,考虑继续优化。

发现手里可用的牌只剩 lcm(u,v)≤max⁡(A,B,C)\text{lcm}(u,v)\le\max(A,B,C)lcm(u,v)≤max(A,B,C) 了,所以考虑从这里下手。

看到 lcm\text{lcm}lcm 就立刻想到了 gcd⁡\gcdgcd。

我们可以考虑枚举 gcd⁡\gcdgcd:

1∼max⁡(A,B,C)1\sim \max(A,B,C)1∼max(A,B,C) 地 O(n)O(n)O(n) 枚举 gcd⁡=x\gcd=xgcd=x,

合法的点对 (u,v)(u,v)(u,v):u=k1x,v=k2xu=k_1x,v=k_2xu=k1​x,v=k2​x

其中 gcd⁡(k1,k2)=1\gcd(k_1,k_2)=1gcd(k1​,k2​)=1

我们需要保证 μ(u)≠0,μ(v)≠0\mu(u)\ne0,\mu(v)\ne0μ(u)​=0,μ(v)​=0 且 (lcm(u,v)=k1k2x)≤max⁡(A,B,C)(\text{lcm}(u,v)=k_1k_2x)\le \max(A,B, C)(lcm(u,v)=k1​k2​x)≤max(A,B,C)

枚举 (k1,k2)(k_1,k_2)(k1​,k2​) 即可。

复杂度为 O(nlog⁡3n)O(n\log ^3n)O(nlog3n)


那么有没有时间复杂度更优的建图方法呢?

有的,O(n)O(n)O(n) 的。


%%%


建图之后据大佬 shadowice1984\mathsf{s{\color{red}hadowice1984}}shadowice1984 亲测只有 7×1057\times 10^57×105 条边

我们可以在 O(mm)O(m\sqrt m)O(mm​) 的复杂度下通过三元环计数从而完成本题。

我们发现上面三元环计数中没有考虑到自环的情况。即三元组 (u,v,w)(u,v,w)(u,v,w) 中存在两个数或者三个数相等的情况,需要我们单独计算。

  • 三个数相同

  • 我们枚举 xxx 直接计算贡献即可。

  • 两个数相同

  • 即 (u,u,v)(u,u,v)(u,u,v) 和 (u,v,v)(u,v,v)(u,v,v),我们可以在枚举两个点 (u,v)(u,v)(u,v) 建图的时候顺便计算一下贡献即可。

  u |  ╲u---vuuvuuvuuvuuv
  • 三个数均不同

  • 枚举三元环计算即可。

  • 我们只需要枚举一个 (u,v,w)(u,v,w)(u,v,w),显然在范围内的所有答案,即三元环 u,v,wu,v,wu,v,w 之间的任意连边顺序均为合法的贡献,即:

    w/   \u --- vuvwuuwvuwuvwwvuwvuwvvwuv

(每个点均有两种选择,共有 2×3=62\times 3=62×3=6 种三元环)

Code

// Problem: P4619 [SDOI2018]旧试题
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4619
// Memory Limit: 500 MB
// Time Limit: 5000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
#define re register
using namespace std;
using ll = long long;
const int N = 6e6 + 7, mod = 1e9 + 7, INF = 0x3f3f3f3f;template <typename T> inline void read(T& t) {int f = 0, c = getchar(); t = 0;while (!isdigit(c)) f |= c == '-', c = getchar();while (isdigit(c)) t = t * 10 + c - 48, c = getchar();if (f) t = -t;
}template <typename T> void print(T x) {if (x < 0) x = -x, putchar('-');if (x > 9) print(x / 10);putchar(x % 10 + 48);
}int n, m, k, t;
int mu[N], primes[N], cnt;
int vis[N], val[N];
ll fa[N], fb[N], fc[N];
int A, B, C;
int minn = INF, maxx;
int edge_cnt;struct Edge
{int u, v, w;
}edge[N];struct Node
{int v, w;
};vector<Node> G[N];
int deg[N];void sieve(int n)
{mu[1] = 1;vis[1] = 1;for(re int i = 2; i <= n; ++ i) {if(vis[i] == 0) {primes[ ++ cnt] = i;mu[i] = -1;}for(re int j = 1; j <= cnt && i * primes[j] <= n; ++ j) {vis[i * primes[j]] = 1;if(i % primes[j] == 0) {mu[i * primes[j]] = 0;break;}mu[i * primes[j]] -= mu[i];}}
}void init()
{for(re int i = 1; i <= maxx; ++ i)fa[i] = 0, fb[i] = 0, fc[i] = 0, deg[i] = 0, G[i].clear();minn = INF;maxx = -INF;edge_cnt = 0;
}void solve()
{   init();read(A), read(B), read(C);maxx = max({maxx, A, B, C});minn = min({minn, A, B, C}); for(re int i = 1; i <= A; ++ i)for(int j = i; j <= A; j += i)fa[i] += A / j;for(re int i = 1; i <= B; ++ i)for(int j = i; j <= B; j += i)fb[i] += B / j;for(re int i = 1; i <= C; ++ i)for(int j = i; j <= C; j += i)fc[i] += C / j;ll ans = 0;//三个数都相同for(re int i = 1; i <= minn; ++ i) {if(mu[i]) ans += 1ll * mu[i] * mu[i] * mu[i] * fa[i] * fb[i] * fc[i];}//建图,顺便把有两个数相同情况的贡献算一下for(re int x = 1; x <= maxx; ++ x) {for(re int k1 = 1; k1 * x <= maxx; ++ k1) {if(mu[k1 * x]) {for(re int k2 = k1 + 1; 1ll * k1 * k2 * x <= maxx; ++ k2) {if(mu[k2 * x] && __gcd(k1, k2) == 1) {int u = k1 * x;int v = k2 * x;int lcm = k1 * k2 * x;int val_u_u = u;int val_v_v = v;int val_u_v = lcm;int val_v_u = lcm;int uuv = mu[u] * mu[u] * mu[v];int uvv = mu[u] * mu[v] * mu[v];ans += uuv * (fa[val_u_u] * fb[val_u_v] * fc[val_v_u] + fa[val_v_u] * fb[val_u_u] * fc[val_u_v] + fa[val_u_v] * fb[val_v_u] * fc[val_u_u]);ans += uvv * (fa[val_v_v] * fb[val_v_u] * fc[val_u_v] + fa[val_u_v] * fb[val_v_v] * fc[val_v_u] + fa[val_v_u] * fb[val_u_v] * fc[val_v_v]);deg[u] ++ ;deg[v] ++ ;++ edge_cnt;edge[edge_cnt].u = u;edge[edge_cnt].v = v;edge[edge_cnt].w = lcm;}}}}}//三个数均不同for(re int i = 1; i <= edge_cnt; ++ i) {int u = edge[i].u, v = edge[i].v, w = edge[i].w;if(deg[u] < deg[v] || (deg[u] == deg[v] && u > v)) swap(u, v);G[u].push_back({v, w});}for(re int x = 1; x <= maxx; ++ x) {if(mu[x]) {for(auto i : G[x])vis[i.v] = x, val[i.v] = i.w;for(auto i : G[x]) {int y = i.v;int val_x_y = i.w;if(mu[y]) {for(auto j : G[y]) {int z = j.v;if(vis[z] == x) {int val_y_z = j.w;int val_x_z = val[z];int val_z_y = val_y_z;int val_z_x = val_x_z;int val_y_x = val_x_y;int muxyz = mu[x] * mu[y] * mu[z];if(muxyz) {ans += muxyz * (fa[val_x_y] * fb[val_y_z] * fc[val_z_x]+ fa[val_x_z] * fb[val_z_y] * fc[val_y_x]+ fa[val_y_z] * fb[val_z_x] * fc[val_x_y]+ fa[val_y_x] * fb[val_x_z] * fc[val_z_y]+ fa[val_z_x] * fb[val_x_y] * fc[val_y_z]+ fa[val_z_y] * fb[val_y_x] * fc[val_x_z]);}}}}}}}print(ans % mod);puts("");
}int main()
{sieve(N - 7);  read(t);while(t -- ) {solve();}return 0;
}

P4619 [SDOI2018]旧试题(莫比乌斯反演,建图优化三重枚举,三元环计数,神仙好题,超级清晰易懂)相关推荐

  1. P4619 [SDOI2018]旧试题

    P4619 [SDOI2018]旧试题 题意: 求个式子: (∑i=1A∑j=1B∑k=1Cd(i∗j∗k))mod(109+7)(\sum_{i=1}^{A}\sum_{j=1}^{B}\sum_{ ...

  2. 牛客挑战赛51 E NIT的gcd(欧拉反演,建图优化,三元环计数)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 Problem 给你一个正整数 nnn. 请你输出 ∑i=1n∑j=1n∑k=1ngcd⁡(i,j)g ...

  3. [SDOI2018] 旧试题

    推狮子的部分 \[ \sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C\sigma(ijk) =\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C\sum_ ...

  4. [SDOI2018]旧试题 题解

    传送门 简单反演+神仙优化题. 首先我们知道 σ0(xy)=∑i∣x∑j∣y[(i,j)=1]\sigma_0(xy)=\sum\limits_{i|x}\sum\limits_{j|y}[(i,j) ...

  5. [bzoj 5332][SDOI2018]旧试题

    传送门 Description \[ \sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^Cd(ijk) (\mathrm{mod\:} 10^9+7) \] 其中 \(d(ijk) ...

  6. 解题报告:NOIP2013 车站分级(拓扑序递推求解差分约束、建图优化O(n+m)) 超详细讲解

    本题是2013年NOIP普及组的压轴题 差分约束裸题. 计算当前线路中最小的级别(比较始发站和终点站). 整条线路中所有大于这个级别的都必须停靠 所有未停靠的站点的级别一定小于这个级别 也就是说所有未 ...

  7. 洛谷 - P1989 无向图三元环计数(思维建图)

    题目链接:点击查看 题目大意:给出一个 nnn 个点 mmm 条边组成的无向图,求三元环的个数 题目分析:对于原图建新图,对于原来的每条边来说 如果度数不同,度数小的点指向度数大的点 如果度数相同,编 ...

  8. codeforces gym100959 I - Robots(稠密图建图优化)

    I - Robots 显然可以两点之间能连边就连边,但是边数会很多,考虑优化 对于三个点(x0,y0)(x_0,y_0)(x0​,y0​),(x0,y1)(x_0,y_1)(x0​,y1​),(x0, ...

  9. P1989 无向图三元环计数 思维 + 建图

    传送门 文章目录 题意: 思路: 题意: 统计无向图中三元环的个数. 思路: 很明显有一种暴力的方法,就是枚举每条边,让后再跑两个点的所有边,可以卡到复杂度O(m2)O(m^2)O(m2). 我们可以 ...

最新文章

  1. GPS部标平台的架构设计(十)-基于Asp.NET MVC构建GPS部标平台
  2. OpenGL编程指南8:模型组合变换
  3. 19-爬虫之scrapy框架大文件下载06
  4. 数据结构之线性查找和折半查找
  5. Java Map接口详解
  6. linux 列出当前视频设备,如何获取Linux(ubuntu)上的视频捕获设备(网络摄像机)列表?(C / C ++)...
  7. 虚拟化与私有云的区别
  8. 腾讯 2016 春季实习校招 HR 面回忆(C++后台)
  9. 青海省多种食品、农产品实现首次出口
  10. appium 原理解析(转载雷子老师博客)
  11. 简易散列表实现电话号码查询系统
  12. modelica练习
  13. docker nginx容器代理bug:upstream server temporarily disabled
  14. DAS、NAS、SAN三种存储架构比较
  15. Java项目毕业设计:基于springboot+vue的电影视频网站系统
  16. 2029年会实现通用人工智能吗?Gary Marcus「叫板」马斯克:赌十万美元如何?
  17. 机器学习面试题(转)
  18. NKOI 3590 循环赛日程表
  19. 一元回归及多元回归模型
  20. 【echarts】在柱状图上方显示数值

热门文章

  1. 链表问题4——反转单向链表
  2. vue-cli3 第三版安装搭建项目
  3. 日志管理之 Docker logs - 每天5分钟玩转 Docker 容器技术(87)
  4. BNUOJ34980方(芳)格(哥)取数(好坑)
  5. Oracle表空间管理
  6. 初始Java DVD项目
  7. 三、openstack安装之Glance篇
  8. ARM Linux (S3C6410架构/2.6.35内核)的内存映射(三)
  9. 如何管理项目中外包开发人员、测试人员
  10. jQuery EasyUI DataGrid 分页 FOR ASP.NET