正题
做法就是设f[i]f[i]f[i]表示i的子树的答案,sum[i]sum[i]sum[i]为iii的(子树的边权和+i到父亲的边权)∗2*2∗2,sz[i]sz[i]sz[i]表示iii子树的的大小。
那么若给xxx的子树一个遍历顺序aaa,那么就有:f[i]=∑i=1sonif[ai]+(∑j=1i−1sum[aj])sz[ai]
f[i]=\sum_{i=1}^{son_i} f[a_i]+(\sum_{j=1}^{i-1} sum[a_j])sz[a_i]
f[i]=i=1∑sonif[ai]+(j=1∑i−1sum[aj])sz[ai]
发现前面这个fff是固定的,可以不用管。
对于后面的东西若sum[ai]sz[ai]>sum[ai+1]sz[ai+1\frac{sum[a_i]}{sz[a_i]}>\frac{sum[a_{i+1}]}{sz[a_{i+1}}sz[ai]sum[ai]>sz[ai+1sum[ai+1],那么更换两个位置产生的上面式子的差值就是sum[ai+1]sz[ai]−sum[ai]sz[ai+1]<0sum[a_{i+1}]sz[a_i]-sum[a_i]sz[a_{i+1}]<0sum[ai+1]sz[ai]−sum[ai]sz[ai+1]<0比原先代价要小。,更换了更优,那么就是按照sum[x]sz[x]\frac{sum[x]}{sz[x]}sz[x]sum[x]来排序。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
int y,nex,c;
}s[N<<1];
struct node{
long long d;
int t;
bool operator<(const node q)const{
return d*q.t<t*q.d;
}
};
long long f[N],sum[N];
int sz[N];
int first[N],len=0,n;
void ins(int x,int y,int c){
s[++len]=(edge){y,first[x],c};first[x]=len;
}
void dfs(int x,int fa){
vector<node> V;sz[x]=1;
for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
dfs(s[i].y,x);
f[x]+=s[i].c*sz[s[i].y]+f[s[i].y],sz[x]+=sz[s[i].y],sum[x]+=2*s[i].c+sum[s[i].y];
V.push_back((node){sum[s[i].y]+2*s[i].c,sz[s[i].y]});
}
sort(V.begin(),V.end());
long long S=0;
for(int i=0;i<V.size();i++) f[x]+=S*V[i].t,S+=V[i].d;
}
int main(){
scanf("%d",&n);
int x,y,c;
for(int i=1;i<n;i++)
scanf("%d %d %d",&x,&y,&c),ins(x,y,c),ins(y,x,c);
dfs(1,0);
printf("%lld\n",f[1]);
}
博客介绍了一种求解子树遍历问题的方法。通过设定f[i]、sum[i]和sz[i],得出子树遍历顺序的表达式。发现前面的f固定可忽略,通过比较sz[x]/sum[x]来排序,若满足一定条件,更换位置代价更小,采用贪心算法求解。
1029

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



