树上最大带权路径长度和

题目:http://codeforces.com/contest/1092/problem/F

分析:拿第一个样例来说,先维护f,s两个数组,f[x]表示以x为根的子树的所有结点到x的带权路径长度和,s[x]表示以x为根的子树所有结点包括x结点的权值和。假设y是x结点的儿子,那么把以y为根的子树当作一个整体,该整体内部的带权路径长度和(内部)+该整体所有结点权值和(外部)即等于以y为根的子树对x产生的带权路径长度和的贡献。

比如图中x=1,y=5,以y为根的子树的所有结点有6,7,8号,到y(5号)结点的WPL和为1*1+6*1+5*1=12,即f[5]=12,而以y为根的子树所有结点权值和为10+1+6+5=22,即s[5]=22,那么以5号结点为根的子树对1号结点产生的WPL贡献为f[5]+s[5]=34,这样可以求出以1号结点为根的子树的WPL=47,然后关键是要选一个根结点使以该根节点为根的子树的WPL最大。

关键步骤:比如刚刚已经求了以1号结点为根的子树的WPL,然后现子求它的邻接点的,此时要对所扩展的邻接点的f,s实施间接更新,具体看代码

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+2;
typedef long long ll;
int val[maxn];
ll _max;
ll f[maxn],s[maxn];
vector<int>edge[maxn];
void dfs(int u,int fa)
{
    s[u]=val[u];
    f[u]=0;
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v!=fa){
            dfs(v,u);
            f[u]+=f[v]+s[v];
            s[u]+=s[v];
        }
    }
}
void dfs(int u,int fa,ll updates,ll updatef)///updates,updatef分别表示对s,f数组来间接更新
{
    _max=max(_max,f[u]+updatef);
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v!=fa){
            ll ups=updates+s[u]-s[v];//s数组的间接更新
            dfs(v,u,ups,updatef+f[u]-f[v]-s[v]+ups);
        }
    }
}
int main()
{
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
    }
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    _max=0;
    dfs(1,0);
    dfs(1,0,0,0);
    printf("%lld\n",_max);
}

 

法二:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+2;
typedef long long ll;
ll val[maxn];
ll sum;
ll _max;
vector<int>edge[maxn];
void dfs(int u,int fa,int dp)///求出以1号结点为根结点的WPL和
{
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v!=fa){
            sum+=val[v]*dp;
            dfs(v,u,dp+1);
            val[u]+=val[v];
        }
    }
}
void dfs2(int u,int fa,ll sum)
{
    if(u!=1) sum=val[1]-val[u]+sum-val[u];///算出以其他结点为根结点的WPL和
    _max=max(_max,sum);                   ///,可由其前驱结点算的WPL来算出
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v!=fa) dfs2(v,u,sum);
    }
}
int main()
{
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&val[i]);
    }
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    _max=0;
    sum=0;
    dfs(1,0,1);
    dfs2(1,0,sum);
    printf("%lld\n",_max);
    return 0;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值