题目链接:
https://www.luogu.com.cn/problem/P1351
参考博客:
https://modeststarlight.blog.luogu.org/solution-p1351
链式前向星:特别特别推荐再学习一下链式前向星
https://blog.csdn.net/acdreamers/article/details/16902023
思路:
1:树状图:只要题目中出现:无向连通图 G有 n 个点,n−1 条边+每条边的长度均为 1,你就应该直接想到这个图其实是一棵树
2:在无边权的树上随意指定一个节点为根,那么我们会发现树上距离为2的节点只有两种情况:
1:两个节点为“祖父-孙子”
2:2.两个节点互为兄弟
3:“祖父-孙子”这种情况比较好解决,在dfs便利树的时候不仅仅传递父亲(f),还把祖父(g)一起传递,那么联合权值就为w[r]*w[g](记录总和时要乘2)
4:那么我们看看兄弟情况该如何解决:设一个节点r的儿子分别是son[1],son[2],son[3]...那么他们的最大值显然是son中最大的w乘上第二大的w,用maxf和maxs分别记录一下最大值和次大值就可以了,总和也很好搞,记录一下son中w总和,平方一下,再减去son[i]与son[i](自己配自己)这样不合法的情况即可,这些都是可以在dfs时顺道完成的,时间复杂度就是O(n)
核心代码:链式前向星
void add(int x,int y)
{
++cnt;
to[cnt]=y;
next[cnt]=head[x];
head[x]=cnt;
}
1:head[i]:它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实在以i为起点的所有边的最后输入的那个编号
2:cnt给每一条输入的边编号
3:next[i]数组存:表示与第i条边同起点的下一条边的存储位置
4:to[i]:表示第i条边的终点
for(int i=head[r];i;i=next[i])
遍历以r节点为起始位置的所有边
#include <bits/stdc++.h>
using namespace std;
#define mod 10007
#define N 200001
#define M 400001
int n,w[N],ans1,ans2,head[N],next[M],to[M],cnt,a,b;
void add(int x,int y)
{
++cnt;
to[cnt]=y;
next[cnt]=head[x];
head[x]=cnt;
}
void dfs(int r,int f,int g)
{
int maxf=0,maxs=0,sum=0,squ=0;
for(int i=head[r];i;i=next[i])
{
int v=to[i];
if(v!=f)
{
sum=(sum+w[v])%mod;
squ=(squ+w[v]*w[v]%mod)%mod;
if(w[v]>maxf)maxs=maxf,maxf=w[v];
else
if(w[v]>maxs)maxs=w[v];
dfs(v,r,f);
}
}
ans1=max(ans1,max(maxs*maxf,w[r]*w[g]));
ans2=(ans2+(sum*sum%mod-squ+mod)%mod+w[r]*w[g]*2%mod)%mod;
}
int main()
{
ios::sync_with_stdio(0);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
dfs(1,0,0);
printf("%d %d\n",ans1,ans2);
return 0;
}