[换根DP] P1364 医院设置

其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为1。如上图中,

若医院建在1 处,则距离和=4+12+2*20+2*40=136;若医院建在3 处,则距离和=4*2+13+20+40=81……

 对于每个根可以很容易的通过一遍dfs获得答案

然后考虑换根,对于根u v 将根从u换到v后v子树中的每个节点距离新根的距离都要减一,对于原根的除v外的子树的距离都要加一, 实际上等价于减去一遍子v树的总权值,加上一遍除v外子树的权值。然后更新答案即可

/*
    Zeolim - An AC a day keeps the bug away
*/
 
//#pragma GCC optimize(2)
//#pragma GCC ("-W1,--stack=128000000")
#include <bits/stdc++.h>
using namespace std;
#define mp(x, y) make_pair(x, y)
#define fr(x, y, z) for(int x = y; x < z; ++x)
#define pb(x) push_back(x)
#define mem(x, y) memset(x, y, sizeof(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef std::pair <int, int> pii;
typedef std::vector <int> vi;
//typedef __int128 ill;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 386910137;
const ull P = 13331; 
const int MAXN = 1e6 + 100;
vector <int> edge[MAXN];
ll val[MAXN] = {0};
ll rans[MAXN] = {0};
ll size[MAXN] = {0};
ll n;
void dfs(int now, int fa, int deep)
{
	size[now] += val[now];
	rans[1] += val[now] * deep;
	for(auto x : edge[now])
	{
		if(x != fa)
		{
			dfs(x, now, deep + 1);
			size[now] += size[x];
		}
	}
}
void gao(int now, int fa)
{
	for(auto x : edge[now])
	{
		if(x != fa)
		{
			rans[x] = rans[now];
			rans[x] -= size[x];
			rans[x] += size[1] - size[x];
			gao(x, now);
		}
	}
}
int main()
{  
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    //freopen("d:\out.txt","w",stdout);
    //freopen("d:\in.txt","r",stdin);
    
    cin >> n;
    
    int ls, rs;
    
    for(int i = 1; i <= n; ++i)
    {
    	cin >> val[i] >> ls >> rs;
    	
    	if(ls)
    		edge[i].push_back(ls), edge[ls].push_back(i);
    	if(rs)
    		edge[i].push_back(rs), edge[rs].push_back(i);
	}
	
	dfs(1, 0, 0);
	
	gao(1, 0);
	
	ll ans = INF;
	
	for(int i = 1; i <= n; ++i)
	{
		ans = min(ans, rans[i]);
	}
	
	cout << ans << '\n';
    
    
	return 0;
}

 

posted @ 2019-10-30 22:22  张浦  阅读(108)  评论(0编辑  收藏  举报