题目描述
无向连通图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)为兄弟关系。对于爷孙关系的点,我们可以划分状态,以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;
}