题意:
给定 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;
}