不用莫反,不用并查集,不用点分治
不用莫反,不用并查集,不用点分治。
首先恰好显然不好做,统计其倍数,然后一一减掉。
即,如果 $d$ 的答案是 $f_d$,现在的 $f_d$ 事实上是 $\sum_{d | x} f_x$,其中的 $f$ 就是所求答案,则
for (int i = m; i >= 1; i--) {
for (int j = i + i; j <= m; j += i)
f[i] -= f[j];
}
即可。
下一步是统计答案。显然统计各连通块大小就行了。你寻思怎么才能保证复杂度,一拍脑门想到一个点的父亲是唯一的,那不就把一个点贡献给父亲就行了。为了保证一个点的贡献被计算完才会给贡献,把点按深度从大到小排个序不就行了。
综上,只用一点简单的算法,这题做完了。这个做法的复杂度是 $O(\sum d(a_i)\log n)$,瓶颈排序,但较难卡满。
按理说会好写多的,但是只比点分治少了 0.5k,怎么会是呢?
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int M = 3e5 + 5;
int a[M], fa[M];
LL f[M], g[M];
int n, dep[M], siz[M], m = 2e5;
vector<int> e[M], fac[M], d[M];
bool in[M], vis[M];
void pre(int n = 2e5) {
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j += i)
fac[j].push_back(i);
}
int main() {
scanf("%d", &n); pre();
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
for (auto x : fac[a[i]]) d[x].push_back(i);
}
for (int i = 1; i < n; i++) {
int u, v; scanf("%d %d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
auto dfs = [&](auto self, int u, int f) -> void {
fa[u] = f, dep[u] = dep[f] + 1;
for (auto v : e[u]) {
if (v == f) continue;
self(self, v, u);
}
} ;
dfs(dfs, 1, 0);
for (int i = 1; i <= m; i++) {
for (auto u : d[i]) in[u] = 1, siz[u] = 1;
sort(d[i].begin(), d[i].end(), [&](int u, int v) -> bool { return dep[u] > dep[v]; });
for (auto u : d[i])
if (in[fa[u]]) siz[fa[u]] += siz[u], siz[u] = 0;
for (auto u : d[i])
f[i] += 1ll * siz[u] * (siz[u] + 1) / 2;
for (auto u : d[i]) in[u] = 0, siz[u] = 0;
}
for (int i = m; i >= 1; i--) {
for (int j = i + i; j <= m; j += i)
f[i] -= f[j];
}
for (int i = 1; i <= m; i++) if (f[i] > 0)
printf("%d %lld\n", i, f[i]);
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/17914829.html

浙公网安备 33010602011771号