CF990G GCD Counting(树上莫比乌斯反演,分层图,并查集)
整理的算法模板合集: ACM模板
点我看算法全家桶系列!!!
实际上是一个全新的精炼模板整合计划
Problem
给定一棵点带权无根树,对于每个 k∈[1,2×105]k\in[1,2\times10^5]k∈[1,2×105],求出有多少个无序点对 (x,y)(x,y)(x,y) 满足 xxx 到 yyy 的简单路径(链)上的所有节点的点权的 gcd\gcdgcd 等于 kkk。(点对中的 xxx 可以等于 yyy,即单点也算作一个答案)
1≤n,ai≤2×1051\le n,a_i\le 2\times 10^51≤n,ai≤2×105
Input
3
1 2 3
1 2
2 3
Output
1 4
2 1
3 1
Solution
定义树上 gcd(x,y)\gcd(x,y)gcd(x,y) 表示 从点 xxx 到点 yyy 之间的简单路径(链)经过的所有点的点权的 gcd\gcdgcd。
设 ans=f(k)ans=f(k)ans=f(k) ,其中 f(n)f(n)f(n) 表示题目中想要求的树上所有 gcd(x,y)=n\gcd(x,y)=ngcd(x,y)=n 的点对 (x,y)(x,y)(x,y) 的数量。显然有些难以下手,因为这里求的是 gcd=n\gcd=ngcd=n,已经固定了,也就是说我们需要算出所有的 gcd\gcdgcd,显然是 O(n2)O(n^2)O(n2) 的复杂度。
树上问题比较抽象,不能很简练的列举出数学公式,考虑 gcd\gcdgcd 计数问题尝试直接使用莫比乌斯反演公式解决。我们构造函数 F(n)=∑n∣df(d)F(n)=\sum\limits_{n\mid d}f(d)F(n)=n∣d∑f(d)
显然 F(n)F(n)F(n) 的实际含义为 n∣gcd(x,y)n\mid\gcd(x,y)n∣gcd(x,y) 的点对 (x,y)(x,y)(x,y) 的数量,即所有简单路径(链)的 gcd(x,y)\gcd(x,y)gcd(x,y) 中有因子 nnn 的点对的数量。
此时根据莫比乌斯反演公式有:
f(n)=∑n∣dF(d)μ(dn)f(n)=\sum_{n\mid d}F(d)\mu(\dfrac{d}{n}) f(n)=n∣d∑F(d)μ(nd)
因此我们只需要计算 F(i),i∈[1,max{a[i]}]F(i),i\in[1,\max\{a[i]\}]F(i),i∈[1,max{a[i]}] ,然后 O(nlogn)O(nlogn)O(nlogn) 卷积计算 f(i)f(i)f(i) 即可。(gcd(x,y)≤max(a[i])\gcd(x,y)\le \max(a[i])gcd(x,y)≤max(a[i]))
因为 F(n)F(n)F(n) 的含义为所有简单路径(链)的 gcd(x,y)\gcd(x,y)gcd(x,y) 中有因子 nnn 的点对的数量,所以我们可以从因子的角度出发,求出所有点权的因子,因为点对 (x,y)(x,y)(x,y) 中 xxx 可以等于 yyy,所以 F(因子)++
,然后对于所有的因子建分层图,每次处理一个因子。
对于因子 xxx,我们将 n−1n-1n−1 条边 (u,v)(u,v)(u,v) 中所有 gcd(a[u],a[v])\gcd(a[u],a[v])gcd(a[u],a[v]) 含有因子 xxx 的边一起构建成一个分层图里,则该分层图中的所有边均有因子 xxx,在建图连边的过程中,对于两条链(连通块) A,BA,BA,B,我们需要计算有多少个点对,所以可以用并查集维护每个连通块的点的个数 size
,使用并查集将他们连起来的时候,F(x)F(x)F(x) 得到的新的贡献(新的点对)显然为 size[A]×size[B]size[A]\times size[B]size[A]×size[B](AAA 中每一个点均可在 BBB 中任意选择一个点凑成一个新的点对)。对于所有因子执行这一过程,我们就可以计算出所有的 F(i)F(i)F(i)。
最后分析一下复杂度:1∼2×1051\sim 2\times 10^51∼2×105 中约数最多仅有 160160160 个,即每条边最多会在 160160160 个分层图里出现,我们先对每条边分解因子然后构建分层图,然后再枚举分层图使用并查集建图,复杂度为 O(n×max{160,n})O(n\times \max\{160, \sqrt{n}\})O(n×max{160,n})
Code
// Problem: G. GCD Counting
// Contest: Codeforces - Educational Codeforces Round 45 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/990/G
// Memory Limit: 256 MB
// Time Limit: 4500 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 7, M = 2e3 + 7;int n, m, k, t;
int a[N];
int primes[N], cnt, mu[N];
bool vis[N];
int fa[N], siz[N];
ll f[N], F[N];
int u[N], v[N];
int maxx;
vector <int> c[N];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);
} void init(int n)
{vis[1] = 1; mu[1] = 1;for(int i = 1; i <= n; ++ i) {if(vis[i] == 0) {primes[ ++ cnt] = i;mu[i] = -1;}for(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];}}
}int Find(int x)
{if(fa[x] == x) return x;return fa[x] = Find(fa[x]);
}void Union(int d, int x, int y)
{int fx = Find(x);int fy = Find(y);if(fx == fy) return ;F[d] += 1ll * siz[fx] * siz[fy];fa[fx] = fy;siz[fy] += siz[fx];
}signed main()
{init(N - 7);read(n);for(int i = 1; i <= n; ++ i) {read(a[i]);maxx = max(maxx, a[i]);int x = a[i];for(int j = 1; j * j <= x; ++ j) {if(x % j == 0) {F[j] ++ ;if(j * j != x) F[x / j] ++ ;}} }/*for(int i = 1; i <= 2e5; ++ i) {if(F[i]) cout << F[i] << endl;}*/for(int i = 1; i <= n - 1; ++ i) {int x, y;read(x), read(y); u[i] = x, v[i] = y;int gcd = __gcd(a[x], a[y]);for(int j = 1; j * j <= gcd; ++ j) {if(gcd % j == 0) {c[j].push_back(i);if(j * j != gcd) c[gcd / j].push_back(i);}} }int limit = 2e5;for(int i = 1; i <= limit; ++ i) {for(int j = 0; j < c[i].size(); ++ j) {fa[u[c[i][j]]] = u[c[i][j]];siz[u[c[i][j]]] = 1;fa[v[c[i][j]]] = v[c[i][j]];siz[v[c[i][j]]] = 1;}for(int j = 0; j < c[i].size(); ++ j) {Union(i, u[c[i][j]], v[c[i][j]]);}}for(int i = 1; i <= limit; ++ i) {for(int j = i; j <= limit; j += i) {f[i] += 1ll * F[j] * mu[j / i];} } for(int i = 1; i <= limit; ++ i) {if(f[i]) {print(i);putchar(' ');print(f[i]);puts("");}}return 0;
}
CF990G GCD Counting(树上莫比乌斯反演,分层图,并查集)相关推荐
- java并查集找朋友圈_图—并查集(解决朋友圈问题)
图也是一种 非线性结构,是由多个顶点组成的关系集合组成的一种数据结构.图可以分为两种,无向图和有向图. ★图的定义: ★典型问题: 利用图能够解决很多问题,这里有一个较为典型的问题,假如已知有n个人和 ...
- 【Project Euler】530 GCD of Divisors 莫比乌斯反演
[题目]GCD of Divisors [题意]给定f(n)=Σd|n gcd(d,n/d)的前缀和F(n),n=10^15. [算法]莫比乌斯反演 [题解]参考:任之洲数论函数.pdf 这个范围显然 ...
- 数学--数论--HDU 4675 GCD of Sequence(莫比乌斯反演+卢卡斯定理求组合数+乘法逆元+快速幂取模)
先放知识点: 莫比乌斯反演 卢卡斯定理求组合数 乘法逆元 快速幂取模 GCD of Sequence Alice is playing a game with Bob. Alice shows N i ...
- 牛客 - 牛半仙的妹子图(并查集+bitset/克鲁斯卡尔重构树+主席树)
题目链接:点击查看 题目大意:给出一个由 n 个点和 m 条边组成的连通图,每个点都有一种颜色,每条边都有一个权值,现在规定一个起点 st,再给出 q 次询问,每次询问给出区间 [ l , r ] , ...
- CF650E Clockwork Bomb(树上构造类问题、并查集)
Description 给出两棵 n 结点的有标号树. 每次操作删去第一棵树的一条边,再加上一条边,需要保证此时还是一棵树. 构造一种操作序列,将第一棵树变成第二棵树,使得操作数最小. n ≤ 5×1 ...
- 杭电1232(图—并查集)
畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- BZOJ 3930 Luogu P3172 选数 (莫比乌斯反演)
BZOJ 3930 Luogu P3172 选数 (莫比乌斯反演) 手动博客搬家:本文发表于20180310 11:46:11, 原地址https://blog.csdn.net/suncongbo/ ...
- 【算法训练营】 - ⑩ 并查集与图
[算法训练营] - ⑩ 并查集与图 并查集 并查集特征 并查集的优化 图 图结构的表达 图的面试题如何搞定? 图的数据结构 点 边 图 生成图 图算法 广度优先遍历 深度优先遍历 图的拓扑排序算法 最 ...
- 牛客挑战赛51 E NIT的gcd(欧拉反演,建图优化,三元环计数)
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 Problem 给你一个正整数 nnn. 请你输出 ∑i=1n∑j=1n∑k=1ngcd(i,j)g ...
最新文章
- window下python扩展包大全
- SharePoint 2007/2010 的SPGridView 控件常见的两个问题
- 循环神经网络 (RNN)
- Codeforces 1188E Problem from Red Panda (计数)
- 人身三流指什么_电气隔离是什么意思呢?
- Boost:glob测试程序
- 我的Linux系统入坑之路!!!!
- ubuntu安装jdk,ubuntu设置java环境变量
- [C++11]lambda表达式语法
- Ubuntu16.04通过GPT挂载硬盘
- java如何关闭一个线程_如何关闭一个java线程池
- 混合精度训练amp,torch.cuda.amp.autocast():
- AudioRecord 采集音频PCM数据
- C常用的字符串函数实现
- shell脚本学习(3)文件判断
- carbon----代码美化工具
- K核苷酸频率(KNF,k-nucleotide frequencies)或K-mer频率
- Unity问题(1)——mesh法线反转
- 品质担当,有效提升办公生产力,永艺XY人体工学椅开箱实测
- 任意进制加法计数器电路设计