[NOIP2014 提高组] 联合权值 题解

题目传送门

本题注意两个点:

  1. 如果 (u,v) 会产生联合权值,(v,u) 也会产生相等的联合权值。
  2. 只有联合权值之和才 mod10007,最大联合权值不用(大坑点)。

对于求最大联合权值,相对简单。因为能产生联合权值的 (u,v) 距离为 2,所以中间必然有一个中转点 wu,w 相邻,v,w 相邻)。我们遍历整棵树,依次把遍历到的节点作为中转点。显然,以中转点 w 为中转点的最大联合权值为与 w 相邻的权值最大次大节点权值的乘积。

for(int w = 1;w <= n;w++){//中转点
	int max1 = -1,max2 = -1;//最大值、次大值
	for(int i = head[w];i;i = e[i].nex){//遍历所有与w相邻的点
		int v = e[i].to;
        //打擂台找最大、次大(请自行分类讨论)
		if(a[v] > max1){
			max2 = max1;
			max1 = a[v];
		}
		else if(a[v] > max2)
			max2 = a[v];
	}
	ans_max = max(ans_max,max1 * max2);
}

对于联合权值之和,我们需要这么分析:

对于与当前中转点相邻的一对 (u,v),其会产生 2×Wu×Wv 的联合权值(还要算上 (v,u) 的),其等于 (Wu+Wv)2(Wu2+Wv2)

对于与当前中转点相邻的三个点 (u,v,x),其共会产生 2×Wu×Wv+2×Wu×Wx+2×Wv×Wx=(Wu+Wv+Wx)2(Wu2+Wv2+Wx2)

依次类推,所有与 w 相邻的点 ui 共会产生 (ui)2(ui2)

因此,我们遍历所有与 w 相邻的点,维护这些点权值和的平方 sum1权值的平方和 sum2,最后将联合权值之和加 sum1sum2

代码如下,注意要边加边取模:

for(int w = 1;w <= n;w++){//中转点
	int sum1 = 0,sum2 = 0;//相邻节点权值和的平方、相邻节点权值的平方和
	for(int i = head[w];i;i = e[i].nex){//遍历所有与w相邻的点
		(sum1 += a[v]) %= MOD;
		(sum2 += a[v] * a[v]) %= MOD;
	}
	sum1 = sum1 * sum1 % MOD;
	(ans_tot += sum1 - sum2) %= MOD;
}

合并后完整代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9,MOD = 10007;
struct edge{
	int to,nex;
} e[N << 1];//建的是无向树,开两倍空间
int head[N],ecnt;
void addedge(int u,int v){//建边
	ecnt++;
	e[ecnt] = (edge){v,head[u]};
	head[u] = ecnt;
}
int n;
int a[N];
int ans_max,ans_tot;//最大联合权值,联合权值之和
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(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 >> a[i];
	for(int w = 1;w <= n;w++){//中转点
		int max1 = -1,max2 = -1;//最大值、次大值
		int sum1 = 0,sum2 = 0;//相邻节点权值和的平方、相邻节点权值的平方和
		for(int i = head[w];i;i = e[i].nex){//遍历所有与w相邻的点
			int v = e[i].to;
			if(a[v] > max1){
				max2 = max1;
				max1 = a[v];
			}
			else if(a[v] > max2)
				max2 = a[v];
			(sum1 += a[v]) %= MOD;
			(sum2 += a[v] * a[v]) %= MOD;
		}
		sum1 = sum1 * sum1 % MOD;
		(ans_tot += sum1 - sum2) %= MOD;
		ans_max = max(ans_max,max1 * max2);
	}
	cout << ans_max << ' ' << (ans_tot % MOD + MOD) % MOD;
	return 0;
}
posted @   5t0_0r2  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示