Loading

不用莫反,不用并查集,不用点分治

不用莫反,不用并查集,不用点分治。

首先恰好显然不好做,统计其倍数,然后一一减掉。

即,如果 $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]);
}
posted @ 2023-03-26 11:37  purplevine  阅读(14)  评论(0)    收藏  举报  来源