好久不发博客了,学弟挂了一组NOIP的题,第一次做这种赛制。。
这个题是求一棵树上,所有距离为2的点对的点权的乘积的和,以及所有距离为2的点对的点权的乘积的最大值。开始用暴力写的,发现在一种特殊的情况下,所有点都和一个点相邻,复杂度就会达到平方,就会超时。
经过优化后的做法是这样的。首先把树转为有根树,记录每个点的父亲,和所有孩子。一个点的任意两个孩子之间的距离一定是2,一个点与父亲的父亲距离一定是2。在求解一个点的2个孩子的权的乘积时,运用一点小技巧,就可以了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <deque>
using namespace std;
const int mod = 10007;
vector<int> E[200010];
bool vis[200010];
int w[200010];
int fa[200010];
int lv[200010];
vector<int> sons[200010]; //存的是点所有孩子的权
int MAX;
int cur;
long long tot;
int z;
void dfs(int u,int pre,int dep){
int sz=E[u].size();
lv[u]=dep;
fa[u]=pre;
for(int i=0;i<sz;i++){
if(E[u][i]==pre)continue;
sons[u].push_back(w[E[u][i]]);
dfs(E[u][i],u,dep+1);
}
}
int main(){
MAX=tot=0;
int n;
cin>>n;
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
E[u].push_back(v);
E[v].push_back(u);
}
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
}
dfs(1,0,0);
for(int i=1;i<=n;i++){
long long sum=0;
//先全加起来,以降低复杂度
for(int j=0;j<sons[i].size();j++){
sum+=sons[i][j];
}
for(int j=0;j<sons[i].size();j++){
sum-=sons[i][j];
tot+=sum* sons[i][j];
tot%=mod;
sum+=sons[i][j];
}
sort(sons[i].begin(),sons[i].end());
if(sons[i].size()>1){
MAX=max(MAX, sons[i][sons[i].size()-1] * sons[i][sons[i].size()-2] );
}
}
for(int i=1;i<=n;i++){
if(lv[i]>=2){
int wx=w[i];
int wy=w[fa[fa[i]]];
int tmp=wx*wy;
MAX=max(MAX,tmp);
tot+=tmp;
tot+=tmp;
tot%=mod;
}
}
cout<<MAX<<" "<<(tot)%mod<<endl;
return 0;
}
本文讨论了如何优化解决树形结构中寻找所有距离为2的点对的点权乘积和最大乘积的问题。通过将树转化为有根树并记录节点信息,实现复杂度优化,避免了平方复杂度导致的超时问题。
366

被折叠的 条评论
为什么被折叠?



