P1351 [NOIP2014 提高组] 联合权值
P1351 [NOIP2014 提高组] 联合权值
题目翻译:
给定一个\(n\)个节点,\(n-1\)条边的无向连通图。及一颗无根树。求任意两个相距为\(2\)的点求其联合权值\(w_i \times w_j\),输出所有联合权值的和对\(10007\)取模后的结果和最大值(注意:只有和需要取模,而最大值不需要)
思路:
\(1\).若按照普通算法枚举任意两个点,则复杂度为\(n^2\),一定会\(TLE\)。因此我们分析发现,任意两点间距\(2\),那一定中间有一个点连接它们,这样我就只需要枚举所有连接了两个点的点即可。
\(2.\)那如何求权值和了?我们设一个点连接两个点\(a,b\),则有联合权值等于:
\[2w_aw_b=(w_a+w_b)^2-(w_a^2+w_b^2) \]设有三个点\(a,b,c\),则:
\[2w_aw_b+2w_aw_c+2w_bw_c=(w_a+w_b+w_c)^2-(w_a^2+w_b^2+w_c^2) \]以此类推可以发现,任意一点做中间点,那它周围所有点的联合权值和等于和的平方减去平方和(注意取模)
\(3.\)求最大值只需要在每个中间点周围点的权值最大和第二大的联合权值求最大即可
完整代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
const int P=10007;
vector<int>e[N];
int w[N];
signed main(){
int n;
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++){
cin>>w[i];
}
int maxn=-2e9,ans=0;
for(int i=1;i<=n;i++){
if(e[i].size()>1){
int sum=0,sub=0;
priority_queue<int>q;
for(auto ed:e[i]){
sum+=w[ed];
sum%=P;
sub+=w[ed]*w[ed]%P;
sub%=P;
q.push(w[ed]);
}
ans=(ans+(sum*sum)%P+P-sub)%P;
ans%=P;
int now=q.top();
q.pop(),now*=q.top();
maxn=max(maxn,now);
}
}
cout<<maxn<<" "<<ans;
}
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18682379
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步