城堡迷阵,51nod1527,贪心

博客介绍了一种求解子树遍历问题的方法。通过设定f[i]、sum[i]和sz[i],得出子树遍历顺序的表达式。发现前面的f固定可忽略,通过比较sz[x]/sum[x]来排序,若满足一定条件,更换位置代价更小,采用贪心算法求解。

正题

做法就是设f[i]f[i]f[i]表示i的子树的答案,sum[i]sum[i]sum[i]iii的(子树的边权和+i到父亲的边权)∗2*22sz[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=1sonif[ai]+(j=1i1sum[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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值