NOIP2014 D1T2 联合权值

10 篇文章 0 订阅

题目描述
无向连通图G 有n 个点,n - 1 条边。点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 。图上两点( u , v ) 的距离定义为u 点到v 点的最短距离。对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu×Wv 的联合权值。
请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

这道题。。暴力10分钟写完70分。我树形DP好弱啊。。
改了好久才AC。我们经观察可以发现,对于可以联合的两个点,他们的关系只有两种:爷孙关系或兄弟关系。
这里的(1,3)(2,4)等点就是爷孙关系。而(6,7)为兄弟关系
这里的(1,3)(2,4)等点就是爷孙关系。而(6,7)为兄弟关系。对于爷孙关系的点,我们可以划分状态,以i为根的最大联合权值与以以i为根的联合权值和。可写出。
爷孙关系:f最大【i】=max{f最大【儿子】,num【自身】*num【最大孙子】}
f和【i】=Σf【儿子】+Σnum【自身】*孙子

兄弟关系:f最大【i】=最大儿子*次大儿子
f和=所有儿子两两排列的乘积。(我们可以只算一次再答案乘2)

代码如下有注释:

#include<bits/stdc++.h>
using namespace std;

const int MAXN=200005;
const int MOD=10007;

struct edge{
    int to,next;
}e[MAXN<<1];

long long head[MAXN],cnt=0,n,tem1,tem2,num[MAXN],maxx=0,sumx=0;
long long ezm[MAXN]/*i的儿子的最大值 */,sumv[MAXN],/*以i为根的联合和*/maxsum[MAXN]/*以i为根的联合最大值。*/,sz[MAXN]; //i的儿子点权值和 
inline void add(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}

void dfs(int u,int fa){
    for(int i=head[u];i;i=e[i].next){//爷孙关系
        int v=e[i].to;
        if(v==fa)continue;
        dfs(v,u);
        ezm[u]=max(ezm[u],num[v]);//找u的儿子的最大值
        maxsum[u]=max(maxsum[u],maxsum[v]);//爷孙关系中最大联合权值
        maxsum[u]=max(maxsum[u],num[u]*ezm[v]);
        sz[u]=(sz[u]+num[v])%MOD;//处理u的所有儿子的值
        sumv[u]=(sumv[u]+sumv[v])%MOD;
        sumv[u]=(sumv[u]+(num[u]*sz[v]%MOD))%MOD;//爷孙关系的联合权值和
    }
    long long tem=0,tem2=0;
    for(int i=head[u];i;i=e[i].next){//兄弟关系
        int v=e[i].to;
        if(v==fa)continue;
        maxsum[u]=max(maxsum[u],tem2*num[v]);
        sumv[u]=(sumv[u]+tem*num[v])%MOD;
        tem2=max(num[v],tem2);//总会乘出最大的。
        tem=(tem+num[v])%MOD;//乘完累加起来,下次再乘的时候包含了之前的点。
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&tem1,&tem2);
        add(tem1,tem2);
        add(tem2,tem1);
    }
    for(int i=1;i<=n;i++)scanf("%d",&num[i]);
    dfs(1,1);
    printf("%d %d\n",maxsum[1],(sumv[1]<<1)%MOD);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值