CF990G GCD Counting(树上莫比乌斯反演,分层图,并查集)
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF990G GCD Counting(树上莫比乌斯反演,分层图,并查集)相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
Problem
给定一棵点带权无根树,对于每个 k ∈ [ 1 , 2 × 1 0 5 ] k\\in[1,2\\times10^5] k∈[1,2×105],求出有多少个无序点对 ( x , y ) (x,y) (x,y) 满足 x x x 到 y y y 的简单路径(链)上的所有节点的点权的 gcd \\gcd gcd 等于 k k k。(点对中的 x x x 可以等于 y y y,即单点也算作一个答案)
1 ≤ n , a i ≤ 2 × 1 0 5 1\\le n,a_i\\le 2\\times 10^5 1≤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) 表示 从点 x x x 到点 y y y 之间的简单路径(链)经过的所有点的点权的 gcd \\gcd gcd。
设 a n s = f ( k ) ans=f(k) ans=f(k) ,其中 f ( n ) f(n) f(n) 表示题目中想要求的树上所有 gcd ( x , y ) = n \\gcd(x,y)=n gcd(x,y)=n 的点对 ( x , y ) (x,y) (x,y) 的数量。显然有些难以下手,因为这里求的是 gcd = n \\gcd=n gcd=n,已经固定了,也就是说我们需要算出所有的 gcd \\gcd gcd,显然是 O ( n 2 ) O(n^2) O(n2) 的复杂度。
树上问题比较抽象,不能很简练的列举出数学公式,考虑
gcd
\\gcd
gcd 计数问题尝试直接使用莫比乌斯反演公式解决。我们构造函数
F
(
n
)
=
∑
n
∣
d
f
(
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) 中有因子
n
n
n 的点对的数量。
此时根据莫比乌斯反演公式有:
f ( n ) = ∑ n ∣ d F ( d ) μ ( d n ) 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 ( n l o g n ) 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) 中有因子
n
n
n 的点对的数量,所以我们可以从因子的角度出发,求出所有点权的因子,因为点对
(
x
,
y
)
(x,y)
(x,y) 中
x
x
x 可以等于
y
y
y,所以 F(因子)++
,然后对于所有的因子建分层图,每次处理一个因子。
对于因子
x
x
x,我们将
n
−
1
n-1
n−1 条边
(
u
,
v
)
(u,v)
(u,v) 中所有
gcd
(
a
[
u
]
,
a
[
v
]
)
\\gcd(a[u],a[v])
gcd(a[u],a[v]) 含有因子
x
x
x 的边一起构建成一个分层图里,则该分层图中的所有边均有因子
x
x
x,在建图连边的过程中,对于两条链(连通块)
A
,
B
A,B
A,B,我们需要计算有多少个点对,所以可以用并查集维护每个连通块的点的个数 size
,使用并查集将他们连起来的时候,
F
(
x
)
F(x)
F(x) 得到的新的贡献(新的点对)显然为
s
i
z
e
[
A
]
×
s
i
z
e
[
B
]
size[A]\\times size[B]
size[A]×size[B](
A
A
A 中每一个点均可在
B
B
B 中任意选择一个点凑成一个新的点对)。对于所有因子执行这一过程,我们就可以计算出所有的
F
(
i
)
F(i)
F(i)。
最后分析一下复杂度: 1 ∼ 2 × 1 0 5 1\\sim 2\\times 10^5 1∼2×105 中约数最多仅有 160 160 160 个,即每条边最多会在 160 160 160 个分层图里出现,我们先对每条边分解因子然后构建分层图,然后再枚举分层图使用并查集建图,复杂度为 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(以上是关于CF990G GCD Counting(树上莫比乌斯反演,分层图,并查集)的主要内容,如果未能解决你的问题,请参考以下文章
CF EDU 1101D GCD Counting 树形DP + 质因子分解
CF1101D GCD Counting 点分治+质因数分解