P1351 联合权值 题解
简要题意:
给定一棵树,每两个距离为 \(2\) 的点 \(u,v\) 会产生 \(w_u \times w_v\) 的“联合权值”。求 “联合权值” 的和,以及所有联合权值中的最大值。
其实这题作为 \(\text{NOIP 2014tg Day1T2}\),并不难。
首先考虑:距离为 \(2\) 的点只有两种情况:
-
爷爷和孙子的关系。
-
弟弟和哥哥的关系。
具体到树上就是,\(u=fa_{fa_v}\),或者 \(fa_u = fa_v\),都会产生 \(w_u \times w_v\) 的贡献。
对于求最大值,我们可以遍历每个节点 \(i\),统计 \(i\) 的爷爷和自己的贡献,以及所以 \(fa_u = fa_v = i\) 的最大值,取出 \(i\) 儿子中的最大值和次大值即可。
对于求和,我们发现,如果 \(u\) 的第 \(v\) 个儿子为 \(sub_{u,v}\),且共 \(k\) 个儿子,则贡献为:
\[\sum_{i=1}^k \sum_{j=i+1}^k w_{sub_{u,i}} \times w_{sub_{u,j}}
\]
\[=\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}
\]
(两两的乘积,先总的重复计算,减去自己乘自己的,交换 \(\div 2\))
\[= \frac{ \bigg ( \sum_{i=1}^k w_{sub_{u,i}} \bigg )^2 -\sum_{i=1}^k ( w_{sub_{u,i}} )^2}{2}
\]
太精妙了!!!
所以统计 平方和 和 和的平方 即可。平方和和和的平方
时间复杂度:\(O(n)\).
实际得分:\(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;
}
简易的代码胜过复杂的说教。