P1351 联合权值 题解

博客园同步

原题链接

简要题意:

给定一棵树,每两个距离为 2 2 2 的点 u , v u,v u,v 会产生 w u × w v w_u \times w_v wu×wv 的“联合权值”。求 “联合权值” 的和,以及所有联合权值中的最大值。

其实这题作为 NOIP 2014tg Day1T2 \text{NOIP 2014tg Day1T2} NOIP 2014tg Day1T2,并不难。

首先考虑:距离为 2 2 2 的点只有两种情况:

  1. 爷爷和孙子的关系。

  2. 弟弟和哥哥的关系。

具体到树上就是, u = f a f a v u=fa_{fa_v} u=fafav,或者 f a u = f a v fa_u = fa_v fau=fav,都会产生 w u × w v w_u \times w_v wu×wv 的贡献。

对于求最大值,我们可以遍历每个节点 i i i,统计 i i i 的爷爷和自己的贡献,以及所以 f a u = f a v = i fa_u = fa_v = i fau=fav=i 的最大值,取出 i i i 儿子中的最大值和次大值即可。

对于求和,我们发现,如果 u u u 的第 v v v 个儿子为 s u b u , v sub_{u,v} subu,v,且共 k k k 个儿子,则贡献为:

∑ i = 1 k ∑ j = i + 1 k w s u b u , i × w s u b u , j \sum_{i=1}^k \sum_{j=i+1}^k w_{sub_{u,i}} \times w_{sub_{u,j}} i=1kj=i+1kwsubu,i×wsubu,j

= ∑ i = 1 k ∑ j = 1 k w s u b u , i × w s u b u , j − ∑ i = 1 k w s u b u , i × w s u b u , i 2 =\frac{ \sum_{i=1}^k \sum_{j=1}^k w_{sub_{u,i}} \times w_{sub_{u,j}} - \sum_{i=1}^k w_{sub_{u,i}} \times w_{sub_{u,i}} }{2} =2i=1kj=1kwsubu,i×wsubu,ji=1kwsubu,i×wsubu,i

(两两的乘积,先总的重复计算,减去自己乘自己的,交换 ÷ 2 \div 2 ÷2

= ( ∑ i = 1 k w s u b u , i ) 2 − ∑ i = 1 k ( w s u b u , i ) 2 2 = \frac{ \bigg ( \sum_{i=1}^k w_{sub_{u,i}} \bigg )^2 -\sum_{i=1}^k ( w_{sub_{u,i}} )^2}{2} =2(i=1kwsubu,i)2i=1k(wsubu,i)2

太精妙了!!!

所以统计 平方和和的平方 即可。平方和和和的平方

时间复杂度: O ( n ) O(n) O(n).

实际得分: 100 p t s 100pts 100pts.

细节:总和要取模,最大值不用。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll MOD=1e4+7;
const int N=2e5+1;

inline int read(){char ch=getchar();int f=1; while(!isdigit(ch)) {if(ch=='-') f=-f; ch=getchar();}
	   int x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f;}

ll n,w[N];
vector<int> G[N];
vector<int> son[N];
ll ans1,ans2;

inline void dfs(ll dep,ll fa,ll gr) {
//正在处理 dep,父亲节点为 fa,祖父(爷爷) 节点为 gr
	ll zd=0,cd=0,sum1=0,sum2=0;
	for(ll i=0;i<G[dep].size();i++) {
		ll v=G[dep][i];
		if(v!=fa) {
			sum1=(sum1+w[v])%MOD;
			sum2=(sum2+w[v]*w[v]%MOD)%MOD; //两边一起统计
			if(w[v]>zd) cd=zd,zd=w[v];
			else if(w[v]>cd) cd=w[v]; //最大值,次大值
			dfs(v,dep,fa); //往下搜索
		}
	} sum1=(sum1*sum1)%MOD; //和的平方
	ans1=max(ans1,max(cd*zd,w[dep]*w[gr])); //统计最大值
	ans2=(ans2+(sum1+MOD-sum2)%MOD+w[dep]*w[gr]*2%MOD)%MOD; //统计和
}

int main(){
	n=read();
	for(ll i=1;i<n;i++) {
		ll u=read(),v=read();
		G[u].push_back(v);
		G[v].push_back(u);
	} for(ll i=1;i<=n;i++) w[i]=read();
	dfs(1,0,0);
	printf("%lld %lld\n",ans1,ans2);
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值