HDU - 6430 Problem E. TeaTree dfs+归并+假算法 2018多校-10

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiang_6/article/details/81952322

题意:

给定 n 个点的树,每个点有权值,任意两个不同的点可以给他们的 LCA 贡献一个值 就是他们value的 gcd,输出每个点能得到的最大值,如果没有这样的值,输出-1;

思路:

首先这是份假代码?? 4000ms  3000ms AC, 刚刚场上口胡了一下没敢写

我们考虑某个结点,如果这个点能作为某两个点的LCA的话,那一定是他的两棵不同子树上的点,或者是他和他的某个孩子结点,

至于gcd就是两个数共同的最大的因子,先用O(n * log n) 的筛法,把每个数的因子预处理出来,

然后进行dfs,对于id 结点的所有子结点的因子集合合并到 id结点的因子集合,同时计算id结点得到的 最大贡献值ans_id

注意: 因为是假算法,可能会存在 MLE TLE 等情况,所以每次询问完子树后 因子集合清空,而且集合合并的时候用小的往大的里面合并

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;

vector<int> y[maxn];
set<int> S[maxn];

void init() {
  for(int i = 2; i < maxn; ++i) {
    for(int j = i; j < maxn; j += i) {
      y[j].push_back(i);
    }
  }
}
vector<int> son[maxn];
int v[maxn], ans[maxn];

int guib(int f, int id) {
  int flag = 0, res = 1;
  if(S[f].size() < S[id].size()) {
    flag = 1;
    swap(f,id);
  }
  for(auto i : S[id]) {
    if(S[f].count(i)) {
      res = max(res, i);
    } else S[f].insert(i);
  }
  if(flag) swap(S[f],S[id]);
  return res;
}

void dfs(int id) {
  for(auto i : y[v[id]]) {
    S[id].insert(i);
  }
  ans[id] = -1;
  for(auto i : son[id]) {
    dfs(i);
    ans[id] = max(ans[id], guib(id,i));
    S[i].clear();
  }
}

int main() {
  init();
  int n;
  scanf("%d", &n);
  memset(ans, 0, sizeof(int)*n);
  for(int i = 2; i <= n; ++i) {
    int f;
    scanf("%d", &f);
    son[f].push_back(i);
  }
  for(int i = 1; i <= n; ++i) {
    scanf("%d", &v[i]);
  }
  dfs(1);
  for(int i = 1; i <= n; ++i) {
    printf("%d\n", ans[i]);
  }

  return 0;
}

猜你喜欢

转载自blog.csdn.net/xiang_6/article/details/81952322