题意:给你一个棵树,这棵树有n-1条边,其实就是一个n-1的边的图,然后问对于每个节点i,他回溯到根节点的路径经过的节点的最大公约数最大是多少,每一个路径至少可以有一个节点不选。
解法:
DP枚举所有的可能,并把所有的可能取个最大值,因为我们不知道那种可能是最大,于是不能用数组,你不能当前选择最优以后,就一定是最优的。
说到DP,必然有状态这一说法,我们可以定义这样一个二维集合(用set),存下来所有的可能,第一维表示当前节点的编号,第二维表示从这个节点回溯到根节点是否有一个没有用到,比如说对于四个节点的类似链状的树,这时候我们求4回溯到根节点1的最大公倍数,1-2-3-4,tag为1,表示从4这个节点到根节点1,我全部数都用上了,但如果我不选四个中的其中一个,比如我不选3,那么就是1-2-4,这时候我所有节点都没有用上所以状态是假的。
当我们把DP的状态定义好了以后,这样的话,也就成功了一半,接下来考虑转移,我们可以把转移树在草稿纸上画出来,比如对于1-2-3-4这种链状树的情况:
初始状态
/ \
1(1) 1(0)
/ \ \
2(1) 2(0) 2(0)
/ \ \ \
3(1) 3(0) 3(0) 3(0)
/ \ \ \ \
4(1) 4(0) 4(0) 4(0) 4(0)
观察上式,可以观察到1这个状态只能由1更新,而0这个状态只能更新到0,于是转移方程也就出来,当上一个状态的tag为1时,他可以更新到下一个状态的0或者1,就像是1(1)更新到2(1)或者是2(0),而0这个状态只能更新到0这个状态,我们可以发现,每个元素的集合元素的个数与该元素所在层数有一定关系:
集合元素个数==该节点的深度+1;
1 -------------- 2个
/
2 ----------------- 3个
/
3 ----------------- 4个
/
4 ---------------------- 5个
实际上我在本地的CB运行的时候,试图测试一个n=200000的数据,发现到崩溃了,事实上本地的CB好像开不了这么多内存,可是由于这题内存限制这么大,所以set是完全允许的。
接着考虑这道题的复杂度是否在接受的范围内,首先是状态数n*2个,然后转移跟节点所在的层数有关,实际上最坏的情况也就是刚刚所说的链状树的情况,这样的层数会到n,自然复杂度会很高,但一般这题的复杂度还是O(n*logn)。所以不会超出。
最后还要注意一下DP的边界处理,自然就是对于1状态的处理,这个应该是在dfs之前进行处理。
欢迎各位来吐槽。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
#include <set>
#define maxn 200010
using namespace std;
typedef long long LL;
int a[maxn];
vector<int> v[maxn];
set<int> dp[maxn][2];
int ans[maxn];
bool used[maxn];
int n;
int gcd(int a,int b)
{
if(a==0)return b;
return b==0?a:gcd(b,a%b);
}
void dfs(int state)
{
used[state]=true;
for(set<int>:: iteratorit=dp[state][0].begin();it!=dp[state][0].end();it++)
ans[state]=max(*it,ans[state]);
for(set<int>:: iteratorit=dp[state][1].begin();it!=dp[state][1].end();it++)
ans[state]=max(*it,ans[state]);
int len=v[state].size();
for(int i=0;i<len;i++)
{
int cur=v[state][i];
if(used[cur])continue;
for(set<int>:: iteratorit=dp[state][0].begin();it!=dp[state][0].end();it++)
{
dp[cur][0].insert(gcd(*it,a[cur]));
}
for(set<int>:: iterator it=dp[state][1].begin();it!=dp[state][1].end();it++)
{
dp[cur][1].insert(gcd(*it,a[cur]));
dp[cur][0].insert(*it);
}
dfs(cur);
}
}
void init()
{
memset(used,false,sizeof(used));
memset(ans,0,sizeof(ans));
for(int i=0;i<=n;i++)
{
dp[i][0].clear();
dp[i][1].clear();
v[i].clear();
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
// for(int i=1;i<=n;i++)
// a[i]=i;
for(int i=1;i<n;i++)
{
int s,e;
scanf("%d%d",&s,&e);
//s=i;e=i+1;
v[s].push_back(e);
v[e].push_back(s);
}
dp[1][0].insert(0);
dp[1][1].insert(a[1]);
dfs(1);
for(int i=1;i<=n;i++)
printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}