【Copy自某谷题解】P1364 【医院设置】

现有的题解基本是用Floyed或者其他稍优的算法跑的,其时间复杂度均在\(O(n^2)\)以上。

那么问题来了,

你们经历过绝望吗

这题作为我们图论考试的一道题,n的范围直接到了10000,此时N^2的算法也无法AC。

有句写居里夫人的话:“别人摸瓜她寻藤,别人摘叶他问根

我们也要做那个“她”, 不能只满足于通过此题,而且要了解本题的\(O(N)\)算法正解:带权树的重心。

树的重心的定义:

树若以某点为根,使得该树最大子树的结点数最小,那么这个点则为该树的重心,一棵树可能有多个重心。

树的重心的性质:

1、树上所有的点到树的重心的距离之和是最短的,如果有多个重心,那么总距离相等。

2、插入或删除一个点,树的重心的位置最多移动一个单位。

3、若添加一条边连接2棵树,那么新树的重心一定在原来两棵树的重心的路径上。

当然,这题我们只需要用到第一条性质。

怎么求树的重心:

定义几个数组:\(f[u]\)表示以u为根的总距离,\(size[u]\)表示以u为根的子树的大小(结点数,此题每个点要乘以权值,下文结点数均指此)。

显然,\(ans=min(f[i],1<=i<=n)\)

首先我们任意以一个点为根dfs一遍,求出以该点为根的总距离。方便起见,我们就以1为根。

接下来就是转移,对于每个u能达到的点v,有:
\[f[v]=f[u]+size[1]-size[v]-size[v]\]
怎么来的呢?试想,当根从u变为v的时候,v的子树的所有节点原本的距离要到\(u\),现在只要到\(v\)了,每个结点的距离都减少1,那么总距离就减少\(size[v]\),同时,以v为根的子树以外的所有节点,原本只要到\(u\)就行了,现在要到\(v\),每个节点的路程都增加了1,总路程就增加了\(size[1]-size[v]\),其中\(size[1]\)就是我们预处理出来的整棵树的大小,减去\(size[v]\)就是除以v为根的子树以外的结点数。

最后取最小值,得解。时间复杂度\(O(n)\)

附上代码:

#include <cstdio>
#define rep(i, m, n) for(register int i = m; i <= n; ++i)
#define INF 2147483647
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
using namespace std;
inline int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
    return s * w;
}
const int MAXN = 10010;
struct Edge{
    int next, to;
}e[MAXN << 1];
int head[MAXN], num, w[MAXN], n, size[MAXN];
long long ans = INF, f[MAXN];
inline void Add(int from, int to){
    e[++num].to = to;
    e[num].next = head[from];
    head[from] = num;
}
void dfs(int u, int fa, int dep){ //预处理f[1]和size
    size[u] = w[u];
    for(int i = head[u]; i; i = e[i].next){
       if(e[i].to != fa)
         dfs(e[i].to, u, dep + 1), size[u] += size[e[i].to];
    }
    f[1] += w[u] * dep;
}
void dp(int u, int fa){  //转移
    for(int i = head[u]; i; i = e[i].next)
       if(e[i].to != fa)
         f[e[i].to] = f[u] + size[1] - size[e[i].to] * 2, dp(e[i].to, u);
    ans = min(ans, f[u]); //取最小值
}
int a, b;
int main(){
    //Open("hospital");
    ans *= ans;
    n = read();
    rep(i, 1, n){
       w[i] = read();
       a = read(); b = read();
       if(a) Add(i, a), Add(a, i);
       if(b) Add(i, b), Add(b, i);
    }
    dfs(1, 0, 0);
    dp(1, 0);
    printf("%lld\n", ans);
    //Close;
    return 0;
}

原文地址:https://www.cnblogs.com/Qihoo360/p/9468306.html

时间: 2024-10-09 15:05:28

【Copy自某谷题解】P1364 【医院设置】的相关文章

【模板】树的重心 洛谷P1364 医院设置

P1364 医院设置 题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上图中, 若医院建在1 处,则距离和=4+12+2*20+2*40=136:若医院建在3 处,则距离和=4*2+13+20+40=81…… 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数.(n≤100) 接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空

洛谷P1364 医院设置

P1364 医院设置 题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上图中, 若医院建在1 处,则距离和=4+12+2*20+2*40=136:若医院建在3 处,则距离和=4*2+13+20+40=81…… 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数.(n≤100) 接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空

luogu P1364 医院设置

P1364 医院设置 题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上图中, 若医院建在1 处,则距离和=4+12+2*20+2*40=136:若医院建在3 处,则距离和=4*2+13+20+40=81-- 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数.(n≤100) 接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空

洛谷P1364 医院设置(Floyd)

题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上图中, 若医院建在1 处,则距离和=4+12+2*20+2*40=136:若医院建在3 处,则距离和=4*2+13+20+40=81…… 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数.(n≤100) 接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空格(一个或多个)分隔,

洛谷 P1364 医院设置

题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上图中, 若医院建在1 处,则距离和=4+12+2*20+2*40=136:若医院建在3 处,则距离和=4*2+13+20+40=81…… 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数.(n≤100) 接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空格(一个或多个)分隔,

P1364 医院设置

题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上图中, 若医院建在1 处,则距离和=4+12+2*20+2*40=136:若医院建在3 处,则距离和=4*2+13+20+40=81-- 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数.(n≤100) 接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空格(一个或多个)分隔,

【Copy自某谷题解】P1445 【[Violet]樱花】

做了题还是忍不住要写一发题解,感觉楼下的不易懂啊. 本题解使用latex纯手写精心打造. 题意:求\(\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}\)的正整数解总数. 首先,不会线筛素数的先去做下LuoguP3383. 开始推导. \[\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}\] 那么\(\frac{1}{x}\)和\(\frac{1}{y}\)肯定是小于\(\frac{1}{n!}\)的.所以\(x\)和\(y\)肯定都是大于\(n!

【Copy自某谷题解】【[TJOI2007]线段】

裸DP.感觉楼下的好复杂,我来补充一个易懂的题解. f[i][0]表示走完第i行且停在第i行的左端点最少用的步数 f[i][1]同理,停在右端点的最少步数. 那么转移就很简单了,走完当前行且停到左端点,那么一定是从右端点过来的,那么从上一行左端点转移的话就是 f[i][0]=abs(上一行左端点的坐标-本行右端点的坐标+本行线段长度) 从上一行右端点转移同理. 不需要什么判断.边界f[1][0]=r[1]+r[1]-l[1]-1,f[1][1]=r[1]-1,然后直接搞就行了,时间复杂度O(n)

P1364 医院设置(树型结构)

传送门闷闷闷闷闷闷 ~~放一个可爱的输入框.~~ 考虑在O(n)的时间内求数以每个节点为医院的距离和. \(设想一下,如果我们已知以1为根节点的距离和f[1],如何求出子节点呢?\) 当医院从1转换到1的儿子节点2 一.那么2为根的子树节点到医院的距离都减少1 二.其余节点到医院的距离都增加1 所以得出方程\(f[v]=f[u]-size[v]+(size[1]-size[v])\) 其中\(size[i]\)表示以i为根的子树节点数的权值和 #include <bits/stdc++.h>