Codeforces 430 Div 2 C.Ilya And The tree

来源:互联网 发布:淘宝男休闲运动皮鞋 编辑:程序博客网 时间:2024/06/07 05:28

题意:给你一个棵树,这棵树有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;

}

阅读全文
0 0
原创粉丝点击