Problem G. GCD Counting

考虑差分,记录每一个答案 $ans_i$ 为 $gcd$ 为 $i$ 的倍数的方案总数。

每次只需要枚举出连通块,然后直接计算即可。

时间复杂度 $O(n\sqrt n)$

#include <bits/stdc++.h>

using namespace std;

template <typename T> 
void read(T& x) {
  x = 0; 
  char ch = 0;
  for (; !isdigit(ch); ch = getchar());
  for (; isdigit(ch); ch = getchar()) {
    x = x * 10 + (ch & 15);
  }
}

const int N = 2e5 + 5;

struct edge {
  int to, nxt;
} e[N << 1];

int head[N];
int edge_cnt;
int a[N];
vector<int> fac[N];
int vis[N];
long long ans[N];
int sz, dg;

void add_edge(int u, int v) {
  e[++edge_cnt] = (edge){v, head[u]};
  head[u] = edge_cnt; 
}

void divide(int x, int id) {
  for (int i = 1; i * i <= x; ++i) {
    if (x % i == 0) {
      fac[i].emplace_back(id); 
      if (i * i != x) {
        fac[x / i].emplace_back(id); 
      }
    }
  }
}

void dfs(int u, int fa) {
  ++sz;
  vis[u] = dg; 
  for (int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].to;
    if (v != fa && a[v] % dg == 0) {
      dfs(v, u); 
    }
  }
}

int main() {
  int n;
  read(n); 
  for (int i = 1; i <= n; ++i) {
    cin >> a[i];
    divide(a[i], i); 
  }
  for (int i = 1; i < n; ++i) {
    int u, v;
    read(u), read(v); 
    add_edge(u, v); 
    add_edge(v, u); 
  }
  for (dg = 1; dg <= 200000; ++dg) { 
    for (int i = 0, m = fac[dg].size(); i < m; ++i) {
      if (vis[fac[dg][i]] != dg) { 
        sz = 0; 
        dfs(fac[dg][i], 0); 
        ans[dg] += (long long)sz * (sz - 1) / 2 + sz;
      }
    }
  }
  for (int i = 200000; i >= 1; --i) {
    for (int j = i + i; j <= 200000; j += i) {
      ans[i] -= ans[j]; 
    }
  }
  for (int i = 1; i <= 200000; ++i) {
    if (ans[i]) {
      printf("%d %I64d\n", i, ans[i]); 
    }
  }
  return 0; 
}
最后修改:2020 年 08 月 22 日 11 : 45 PM
如果觉得我的文章对你有用,请随意赞赏