【金华集训 && 题解 && 树形动态规划】 Gcd Counting

题目传送门

题目描述:

给出一棵n个节点的树,每个节点上有点权ai。

求最长的树上路径,满足条件:

路径上经过节点(包括两个端点)点权的gcd和不等于1。


题目中的 g c d gcd gcd不为1的限制性很强
也就是说对于一串数,它们只要有一个质因数即可。

经过这番思考,我们自然而然地设置出以下方程:
d p [ i ] [ j ] dp[i][j] dp[i][j]表示在以i为根的子树中至少含有质因数j的最长路径长度

我们可以先预处理出每一个数的因数,这就是合法的位置。

其实有一个贪心,最长链从下往上枚举是最优的(即从儿子转移到父亲)。。这也许很白痴

所以考虑树形dp

  • 对于一个根u,遍历到了它的儿子v
  • 如何通过v来转移u??
  • 枚举u的因数,枚举v的因数
  • 如果u、v的因数相同,既可以通过v来转移u

大致过程如下:
f [ u ] [ i ] = m a x ( f [ u ] [ i ] , f [ v ] [ j ] + 1 ) ( p r [ u ] [ i ] = = p r e [ v ] [ j ] ) f[u][i] = max(f[u][i],f[v][j]+1) (pr[u][i] == pre[v][j]) f[u][i]=max(f[u][i],f[v][j]+1)(pr[u][i]==pre[v][j])

对于变量的问题,具体看程序。


Code

#include<bits/stdc++.h>
using namespace std;
int n;
int a[1010100];
struct node{
    int y,Next;
}e[500010];
int maxx=0 , len=0 ;
int linkk[300010];
vector < int > pr[300010];//第i位的因数
vector < int > dp[300010];//上述dp的概念

void insert(int x,int y){
    e[++len] = (node){y,linkk[x]};
    linkk[x] = len;
}

void dfs(int x,int fa){
	for (int i=linkk[x];i;i=e[i].Next){
	    int y=e[i].y;
	    if (y == fa) continue;
	    dfs(y,x);
	    for (int k=0;k<pr[x].size();k++)
	      for (int j=0;j<pr[y].size();j++)
	        if (pr[x][k] == pr[y][j]){
			    maxx = max(maxx,dp[x][k] + dp[y][j]);//更新maxx
			    dp[x][k] = max(dp[x][k],dp[y][j]+1);//更新dp
			}
	}
}

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),maxx = a[1]!=1;
    for (int i=1,x,y;i<n;i++)
      scanf("%d %d",&x,&y),insert(x,y),insert(y,x);
      
    for (int i=1;i<=n;i++){
	    int x = a[i];
	    for (int j=2;j*j<=a[i];j++)
	      if (x % j == 0){
		      pr[i].push_back(j);
		      dp[i].push_back(1);
		      while (x%j == 0) x/=j;
		  }
		if (x > 1) pr[i].push_back(x),dp[i].push_back(1);
	}//预处理出质因子
	dfs(1,0);
	printf("%d",maxx);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值