返回顶部

【树型dp】联合权值

作者:@魔幻世界魔幻人生
本文为作者原创,转载请注明出处:https://www.cnblogs.com/subtlemaple/p/16424843.html


题意

无向连通图 G 有 n 个点,n−1 条边。点从 1 到 n 依次编号,编号为 i 的点的权值为 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取余。

 


 

重要结论

2ab = (a+b)^{2} - (a^2+b^2)

2ab+2ac+2bc=(a+b+c)^2-(a^2+b^2+c^2)

推论:对于一个集合中的数,二倍两数之积的组合等于所有元素的完全平方和减去其平方之和


此题注意:有序点对(u,v)(v,u) 是不同的点对


代码加注释:

//联合权值 
//
/*
一颗无根无向树上每条边距离都是1,每个点i都有一个权值w[i],
定义两点距离为其最短距离,
若一个有序点对(i,j)距离是2,则它们有联合权值 w[i]*w[j]
求整颗图上最大的联合权值,以及所有联合权值之和
*/

/*
问题一:
我们注意到若以一个点u为根,
则它的任意两个子节点距离都是2,可以形成联合权值 ,
且u和它的祖父也能形成联合权值
那么只需要以任一点u为根节点进行dfs,
找到 u的子节点权值最大的和次大的,
然后用其乘积刷新当前最大权值 ,
再用u和它祖父的乘积刷新最大权值

问题二:
注意点对有序,所以找到所有联合权值和之后要乘2
那么假如两个符合要求的点w分别为a和b,那么2ab就对答案有贡献,
而我们知道任意几个数的完全平方和减去它们的平方之和,就是它们两两组合之积的二倍
那问题就迎刃而解了,注意只对联合权值总和取模,
对最大值不做处理
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define il inline
using namespace std;
const int maxn=2e5+10;
const int mod=10007;
int h[maxn],cnt;
struct edge{
int to,nxt;
}e[maxn<<1];
il void addedge(int x,int y)
{
cnt++;
e[cnt].to=y; e[cnt].nxt=h[x]; h[x]=cnt; return;
}

int w[maxn];
int n;
int ans_1,ans_2;

void dfs(int u,int fa,int gf)
{
int sum=0,squ=0,maxx=0,mavv=0;
for(int i=h[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
sum=(sum+w[v])%mod;
squ=(squ+w[v]*w[v]%mod)%mod;
if(w[v]>maxx)
{
mavv=maxx; maxx=w[v];
}
else//注意是else
if(w[v]>mavv) mavv=w[v];
dfs(v,u,fa);
}
ans_1=max(ans_1,max(mavv*maxx,w[u]*w[gf]));
   //联合权值最大
ans_2=(ans_2+sum*sum%mod-squ+mod+mod)%mod;
   //计算子节点之间的联合权值之和
ans_2=(ans_2+w[u]*w[gf]*2%mod)%mod;
   //别忘了还有u和爷爷的联合权值没加
}


int main()
{
//freopen("data.in","r",stdin);
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
addedge(u,v);
addedge(v,u);
}
for(int i=1;i<=n;i++)
{
cin>>w[i];
}
dfs(1,0,0);//u,父亲,祖父
cout<<ans_1<<" "<<ans_2;


return 0;
}
posted @   魔幻世界魔幻人生  阅读(130)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示