题目
题意:
给定一棵树,每个点都有权值。每个节点的答案等于节点到根的路径上所有点的权值的gcd,对于每个点,都可以使这条路径上的某个点的权值变为0,要求使得每个节点的答案尽可能大。
分析:
因为是gcd,gcd一个很显然的性质就是这些值的种类并不多,本题的关键点就在这里。所以我们可以暴力去计算,对于每个点维护一个set,存放当前节点到根节点路径上删除某个点后剩下的gcd。在搜索的时候维护这个set就可以了,对于当前节点,就从父节点的set数组中取出数来与当前节点的权值取gcd即可,因为只能删除一次,再加上删除当前节点的gcd即可,这个gcd就递归的时候下传。
#include <iostream>
#include <vector>
#include <set>
using namespace std;
vector<int> g[200005];
set<int> res[200005];
int dp[200005][2],v[200005];
int _gcd(int a,int b)
{
if( b == 0 ) return a;
return _gcd(b,a%b);
}
void dfs(int x,int fa,int gcd)
{
for (int i = 0; i < g[x].size(); i++)
{
int t = g[x][i];
if( t == fa ) continue;
set<int>:: iterator it;
res[t].insert(gcd);
for (it = res[x].begin(); it != res[x].end(); it++)
{
res[t].insert(_gcd(*it,v[t]));
}
dfs(t,x,_gcd(gcd,v[t]));
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> v[i];
}
for (int i = 1; i < n; i++)
{
int x,y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dp[1][0] = v[1];
dp[1][1] = 0;
res[1].insert(0);
dfs(1,0,v[1]);
for (int i = 1; i <= n; i++)
{
if( i == 1 ) cout << v[i];
else cout << *--res[i].end();
if( i == n ) cout << '\n';
else cout << ' ';
}
return 0;
}