题目描述
小美拿到了一棵树,每个节点有一个权值。初始每个节点都是白色。小美有若干次操作,每次操作可以选择两个相邻的节点,如果它们都是白色且权值的乘积是完全平方数,小美就可以把这两个节点同时染红。小美想知道,自己最多可以染红多少个节点?
输入描述:第一行输入一个正整数n,代表节点的数量。 第二行输入n个正整数ai,代表每个节点的权值。 接下来的n−1行,每行输入两个正整数u,v,代表节点u和节点v有一条边连接。1≤n≤10^5 1≤ai≤10^9 ,1≤u,v≤n。
输出描述:输出一个整数,表示最多可以染红的节点数量。
输入
3 3 3 12 1 2 2 3
输出
2 说明:可以染红第二个和第三个节点。请注意,此时不能再染红第一个和第二个节点,因为第二个节点已经被染红。因此,最多染红 2 个节点。
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int N=1e6+5;
inline int read(){
int x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
int a[N],f[N][2];
vector<int>g[N];
void dfs(int x,int y){
vector<int>ve;
int k=0,s=0;
for(int i:g[x]){
if(i==y)
continue;
dfs(i,x);
int sq=sqrt(a[x]*a[i]);
if(sq*sq==a[x]*a[i])ve.push_back(i);
s+=max(f[i][1],f[i][0]);
}
f[x][0]=s;
for(int i:ve){
f[x][1]=max(f[x][1],s-max(f[i][1],f[i][0])+f[i][0]+2);
}
}
int main(){
int n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0);
cout<<max(f[1][0],f[1][1]);
}
-
const int N=1e6+5;
:定义一个常量N
,表示图中顶点的最大数量。 -
inline int read()
:定义一个内联函数read
,用于读取一个整数,内联函数使运行效率高。 -
int a[N],f[N][2];
:定义两个数组a
和f
,a
用于存储每个顶点的值,f
是一个二维数组,用于存储每个顶点的两个不同状态的值。 -
vector<int>g[N];
:定义一个邻接表g
,表示图的邻接关系。 -
void dfs(int x,int y)
:定义一个深度优先搜索函数dfs
,x
是当前顶点,y
是上一个顶点。 -
在
dfs
函数中,首先初始化一个空的vector
ve
,两个整数k
和s
。 -
使用循环遍历当前顶点
x
的所有邻接顶点,如果邻接顶点是上一个顶点y
,则跳过。 -
对每个邻接顶点
i
,递归调用dfs(i,x)
。 -
计算当前顶点
x
和邻接顶点i
的乘积的平方根,如果平方根的平方等于原始乘积,则将i
加入到ve
中。 -
更新
s
为当前顶点所有子树中最大值的最大值。 -
更新
f[x][0]
为s
。 -
循环遍历
ve
中的每个顶点i
,更新f[x][1]
为当前f[x][1]
和s
减去i
的较大值加上i
的较小值再加2的最大值。 -
读取顶点的数量
n
。 -
循环读取每个顶点的值,并存储在数组
a
中。 -
循环读取每条边的信息,构建图的邻接表。
-
从顶点1开始调用
dfs
函数。 -
输出
f[1][0]
和f[1][1]
中的较大值。
找到树中最长的简单路径,f[x][0]
表示以x
为根的子树中,不包含直径的最大边权和,而f[x][1]
表示包含直径的最大边权和。最终输出的是整个树中最长路径的边权和。