【Educational Codeforces Round 58 (Rated for Div. 2) D. GCD Counting】 分解质因子+树形DP

24 篇文章 0 订阅
7 篇文章 0 订阅

题目链接
Educational Codeforces Round 58 (Rated for Div. 2) D. GCD Counting
题意

给你一个n个点的带权树,第i个点的权值为a[i]求一条树上最长的路径,满足路径上所有点权的gcd不为1
1 &lt; = n &lt; = 2 ∗ 1 0 5 1&lt;=n&lt;=2*10^5 1<=n<=2105
1 &lt; = a i &lt; = 2 ∗ 1 0 5 1&lt;=a_i&lt;=2*10^5 1<=ai<=2105
做法

首先要想到,gcd是质因子就足够了,也就是说,肯定存在一条最长路径公约数是质数。
之后对于一个小于 2 ∗ 1 0 5 2*10^5 2105的数,他最多存在7个质因子
这里是因为, 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 = 510510 &gt; 2 ∗ 1 0 5 2*3*5*7*11*13*17=510510&gt;2*10^5 2357111317=510510>2105
所以对于树上的每个点,只需要求这些质因子最多能向下延伸的长度。
我们就dp[i][j]为以i为起点的向i的子树延伸的gcda[i]的第j个质因子的最长链的长度。
那么每次更新dp[i][j]一定是他某个儿子也含有这个质因子,我们想要更新dp[i][j]
就要知道这个质因子是他儿子的第几个质因子,
所以我们预处理的时候用map存储,map[pair<i,j>]=k表示ij中是第k个质因子。
存储质因子用vector存储,G[i][j]表示i的第j个质因子是哪个。
这样我们就可以得到更新dp[i][j]的转移方程
设当前访问的结点为rt,要转移的质因子为第j个,当前访问的儿子为to

	if(a[to]%G[rt][j]==0)
		int pos=mp[pii(G[rt][j],to)];//rt的第j个质因子是to的第pos个质因子
		dp[rt][j]=max(dp[rt][j],dp[to][pos]+1);

类似dp求树的直径,我们对所有儿子中的可更新父亲第j个因子的dp值取前两大
也就是以rt为根的子树内,gcda[rt]j个质因子的最长路径。
最后对每个子树内每个质因子能达到的最长路径取max就是答案。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 2e5+5;
int a[maxn];
int dp[maxn][10];
vector<int> G[maxn];
vector<int> zhuan[maxn];
vector<int> vec[maxn];
map<pii,int>  mp;
int ans=0;
void dfs(int rt,int fa)
{
    for(int i=0;i<G[rt].size();i++) dp[rt][i]=1;
    int maxx1[10],maxx2[10];
    for(int i=0;i<10;i++)
    {
        maxx1[i]=0;
        maxx2[i]=0;
    }
    for(int i=0;i<vec[rt].size();i++)
    {
        int to=vec[rt][i];
        if(to==fa) continue;
        dfs(to,rt);
        for(int j=0;j<G[rt].size();j++)
        {
            if(a[to]%G[rt][j]==0)
            {
                int pos=mp[pii(G[rt][j],to)];//rt的第j个质因子是to的第pos个质因子
                int tmp=dp[to][pos];
                if(maxx1[j]<tmp)
                {
                    maxx2[j]=maxx1[j];
                    maxx1[j]=tmp;
                }
                else if(maxx2[j]<tmp)
                {
                    maxx2[j]=tmp;
                }
            }
        }
    }
    for(int i=0;i<G[rt].size();i++)
    {
        dp[rt][i]+=maxx1[i];
        ans=max(ans,maxx1[i]+maxx2[i]+1);
    }
    return ;
}
int main()
{
    int n,u,v;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        int k=a[i];
        for(int j=2;j*j<=k;j++)
        {
            if(k%j==0)
            {
                G[i].push_back(j);
                mp[pii(j,i)]=G[i].size()-1;
                while(k%j==0) k/=j;
            }
        }
        if(k>1)
        {
            G[i].push_back(k);
            mp[pii(k,i)]=G[i].size()-1;
        }
    }
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u,&v);
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    dfs(1,-1);
    printf("%d\n",ans);
    return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值