洛谷 P1351 联合权值 树状图 链式前向星

题目链接:

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;
}
发布了117 篇原创文章 · 获赞 37 · 访问量 6582

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/104196106