洛谷-1351 联合权值

题目描述
无向连通图 G 有 n 个点,n−1 条边。点从 1 到 n 依次编号,编号为 iii 的点的权值为 Wi,每条边的长度均为 1。图上两点 (u,v)的距离定义为 u 点到 v 点的最短距离。对于图 G 上的点对 (u,v),若它们的距离为 2,则它们之间会产生Wv×Wu​ 的联合权值。
请问图 G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?
输入输出格式
输入格式:
第一行包含 1 个整数 n。
接下来 n−1 行,每行包含 2 个用空格隔开的正整数 u,v,表示编号为 u 和编号为 v 的点之间有边相连。
最后 1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示图 G上编号为 i 的点的权值为 Wi​。
输出格式:
输出共 1 行,包含 2 个整数,之间用一个空格隔开,依次为图 G 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007取余。

输入输出样例
输入样例#1:
5
1 2
2 3
3 4
4 5
1 5 2 3 10

输出样例#1:
20 74

解释:很明显在树上进行dp,每个点维护4个值, s u m 1 [ r o o t ] , s u m 2 [ r o o t ] , M a x 1 [ r o o t ] , M a x 2 [ r o o t ] sum1[root],sum2[root],Max1[root],Max2[root] sum1[root],sum2[root],Max1[root],Max2[root],分别是root孩子的权和,权平方和,最大孩子,次大孩子。那么答案就是 s u m 1 [ r o o t ] ∗ s u m 1 [ r o o t ] − s u m 2 [ r o o t ] , sum1[root]*sum1[root]-sum2[root], sum1[root]sum1[root]sum2[root], 2 ∗ s u m 1 [ s o n ] ∗ a [ r o o t ] 2*sum1[son]*a[root] 2sum1[son]a[root],孩子兄弟之间的贡献,和根节点与子孙节点的贡献,并且维护好最大值就行了,也分两种情况,同上。

#include<iostream>
#define N 200004
#define mod 10007
using namespace std;
int a[N]={0};
int head[N]={0};
int nex[2*N]={0};
int V[2*N]={0};
int tot=0;
int sum1[N]={0};
int sum2[N]={0};
int Max1[N]{0};
int Max2[N]={0};
int ans=0,ret=0;
int n=0;
void add(int x,int y){
    tot++;
    nex[tot]=head[x];
    V[tot]=y;
    head[x]=tot;
}
void DP(int x,int fa){
    for(int i=head[x];i;i=nex[i]){
        int to=V[i];
        if(to==fa) continue;
        sum1[x]+=a[to];
        sum1[x]%=mod;
        sum2[x]+=a[to]*a[to];
        sum2[x]%=mod;
        if(a[to]>Max1[x]){
            Max2[x]=Max1[x];
            Max1[x]=a[to];
        }else if(a[to]>Max2[x]){
            Max2[x]=a[to];
        }
        DP(to,x);
    }
    ret+=sum1[x]*sum1[x]-sum2[x];
    ret%=mod;
    ans=max(ans,Max1[x]*Max2[x]);
    for(int i=head[x];i;i=nex[i]){
        int to=V[i];
        if(to==fa) continue;
        ret+=2*sum1[to]*a[x];
        ret%=mod;
        ans=max(ans,a[x]*Max1[to]);
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=0,x,y;i<n-1;i++){
        cin>>x>>y;add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++) cin>>a[i];
    DP(1,-1);
    cout<<ans<<" "<<ret%mod<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值