齐心抗疫(找树的直径)

题意: 给你一颗n个点的树,树上每个点有一个值,树上每条边长度为1,对于两点x,y之间的代价,x对y的代价可以表示为a[x]*(dis(x,y)),要求任选两点,获得一个最大的价值。

数据范围:n<=50000。

思路: 直接遍历每个点,找距离他最远的点,然后维护最大值 。这个方法只能骗部分分,因为时间复杂度为O(n^2)。

先讲结论: 我们假设这棵树中距离最大的两个点的距离为dismax,这两个点之间的距离称为树的直径,那么对于任何一个点,他在树上距离他最远的点必然是直径的两个端点之一。至于怎么证明,自己思考比较好。我讲的不是很清楚。

找树的直径: 我们先随意以一个点为根,找到离他最远的点,这个点作为端点之一,然后以这个端点为根,找到距离这个根最远的点,这两个点就是直径的端点。

最后我们只要遍历每个点,维护最大值就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int inf=0x7fffffff;
const int mod=1e9+7;
const int eps=1e-6;
typedef long long ll;
typedef unsigned long long ull;
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n'
#define null NULL
int a[N];
int head[N],nxt[N],to[N],tot=0;
void add(int u,int v)
{
    nxt[++tot]=head[u];
    to[tot]=v;
    head[u]=tot;
}
int de[N],dee[N],deep[N];
void dfs(int x,int y,int *d)
{
    d[x]=d[y]+1;
    for(int i=head[x];i;i=nxt[i])
    {
        if(to[i]!=y)
        {
            dfs(to[i],x,d);
        }
    }
}
signed main()
{
    IOS;
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v);add(v,u);
    }
    dfs(1,0,de);
    int ma=0,root1=0;
    for(int i=1;i<=n;i++)
    {
        if(de[i]>ma)
        {
            ma=de[i];
            root1=i;
        }
    }
    dfs(root1,0,dee);
    ma=0;
    int root2=0;
    for(int i=1;i<=n;i++)
    {
        if(dee[i]>ma)
        {
            ma=dee[i];
            root2=i;
        }
    }
    dfs(root2,0,deep);
    int res=0;
    for(int i=1;i<=n;i++)
    {
        res=max(res,max(dee[i]-1,deep[i]-1)*a[i]);
    }
    cout<<res<<endl;
}

发布了93 篇原创文章 · 获赞 9 · 访问量 4203

猜你喜欢

转载自blog.csdn.net/Joker_He/article/details/105332218