题目传送门
题目描述:
给出一棵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;
}