题意
无向连通图 $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取余。
样例输入 #1
5
1 2
2 3
3 4
4 5
1 5 2 3 10
样例输出 #1
20 74
提示
本例输入的图如上所示,距离为2的有序点对有(1,3)、(2,4)、(3,1)、(3,5) 、(4,2)、(5,3) 。
其联合权值分别为2,15,2,20,15,20。其中最大的是20,总和为74。
思路
要求的点对距离为2,我们可以枚举中间点(有点floyed那味了),这样子可以保证距离为2。
中转点周围有n个点,其权值分别为,那么其联合权值为n个权值两两相乘的2倍(数对是可以翻转的),数学好的各位其实不难发现,这个联合权值就是
,因此,我们可以维护
和
,对于最大的联合权值,找到周围最大的两个权值ma1和ma2相乘判断即可。
切记,题目说“由于所有联合权值之和可能很大,输出它时要对10007取余。”,因此对联合权值之和取余,但并不代表对最大联合权值取余!
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int z=500010,mod=10007;
struct edge
{
ll v,next;
}e[2*z];
ll n,head[z],idx=1,u,v,w[z],ma1,ma2,s1,s2,t,s,ma;
void addedge(ll u,ll v)
{
e[idx].v=v;
e[idx].next=head[u];
head[u]=idx;
idx++;
}//邻接表存图
int main()
{
scanf("%lld",&n);
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&u,&v);
addedge(u,v);
addedge(v,u);//无向图建双边
}
for(int i=1;i<=n;i++)
scanf("%lld",&w[i]);
for(int i=1;i<=n;i++)
{
ma1=ma2=0;//ma1最大值,ma2次大值
s1=s2=0;
for(int j=head[i];j!=0;j=e[j].next)
{
t=e[j].v;
if(w[t]>ma1)ma2=ma1,ma1=w[t];
else if(w[t]>ma2)ma2=w[t];
s1=(s1+w[t])%mod,s2=(s2+w[t]*w[t])%mod;//s1各权值之和的平方,s2各权值的平方之和
}
//寻找转接点,则距离肯定不为1
//以某个节点为转接点的权值之和等于权值之和的平方减去权值各自平方之和
s1=s1*s1%mod;
s=(s+mod+s1-s2)%mod;
ma=max(ma,ma1*ma2);
}
printf("%lld %lld",ma,s);
return 0;
}
//随时记得%mod